#!/usr/bin/env php
<?php
/*
* This file is part of Chevere.
*
* (c) Rodolfo Berrios <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
use League\CLImate\CLImate;
use Rodber\Wordle\Compare;
use Rodber\Wordle\Word;
use function Chevere\Filesystem\filePhpReturnForPath;
require_once __DIR__ . '/vendor/autoload.php';
$climate = new CLImate();
$climate->clear();
$climate->addArt(__DIR__ . '/app/banner');
$climate->animation('intro')->speed(200)->enterFrom('top');
$options = [
'en' => 'English',
'es' => 'Español',
'pt' => 'Português',
];
$input = $climate->radio('Language selection:', $options);
$lang = $input->prompt();
$climate->addArt(__DIR__ . "/app/lang/{$lang}/banner");
$en = [
'options' => 'Game options',
'length' => 'Word length [3, 8] (default 5)',
'responses' => 'Responses [0, n] (default 6, no limit 0)',
'looking' => 'Looking for <invert> %length% </invert> char word',
'try' => 'Try',
'must' => 'Word must be %s char length',
'unknown' => 'Unknown word (not in wordlist)',
'welcome' => 'Welcome to Wordle CLI!',
'instructions' => 'You have to guess a word of a certain length.',
'exact_match' => 'Exact match',
'partial_match' => 'Partial (wrong position)',
'no_match' => 'No match',
'tries' => 'tries',
];
$es = [
'options' => 'Opciones del juego',
'length' => 'Largo de palabra [3, 8] (defecto 5)',
'responses' => 'Respuestas [0, n] (defecto 6, sin límite 0)',
'looking' => 'Buscando palabra de <invert> %length% </invert> caracteres',
'try' => 'Intento',
'must' => 'La palabra debe ser de %s caracteres',
'unknown' => 'Palabra desconocida (no está en el diccionario)',
'welcome' => 'Bienvenido a Wordle CLI!',
'instructions' => 'Tienes que adivinar una palabra de cierta longitud.',
'exact_match' => 'Coincidencia exacta',
'partial_match' => 'Parcial (posición incorrecta)',
'no_match' => 'Sin coincidencia',
'tries' => 'intentos',
];
$pt = [
'options' => 'Opções do jogo',
'length' => 'Comprimento da palavra [3, 8] (padrão 5)',
'responses' => 'Respostas [0, n] (padrão 6, sem limite 0)',
'looking' => 'Procurando palavra de <invert> %length% </invert> caracteres',
'try' => 'Tente',
'must' => 'A palavra deve ter %s caracteres',
'unknown' => 'Palavra desconhecida (não está no dicionário)',
'welcome' => 'Bem-vindo ao Wordle CLI!',
'instructions' => 'Você tem que adivinhar uma palavra de certo comprimento.',
'exact_match' => 'Correspondência exata',
'partial_match' => 'Parcial (posição errada)',
'no_match' => 'Sem correspondência',
'tries' => 'tentativas',
];
$LANG = $$lang;
$climate->br()->out('? ' . $LANG['welcome'])->br();
$climate->out($LANG['instructions'])->br();
$climate->out('? ' . $LANG['exact_match']);
$climate->out('? ' . $LANG['partial_match']);
$climate->out('? ' . $LANG['no_match'])->br();
$options = range(3, 8);
$climate->out('<green>' . $LANG['options'] . '</green>');
$input = $climate->input('<green>* ' . $LANG['length'] . ':</green>');
$input->defaultTo('5');
$input->accept($options);
$wordLength = intval($input->prompt());
$input = $climate->input('<green>* ' . $LANG['responses'] .' :</green>');
$input->defaultTo('6');
$ansTries = intval($input->prompt());
$attempts = $ansTries === 0
? '?'
: $ansTries;
$wordListFile = filePhpReturnForPath(__DIR__ . "/app/lang/{$lang}/words/{$wordLength}.php");
$wordList = $wordListFile->cast()->array();
$randomWord = mb_strtoupper($wordList[rand(0, count($wordList))]);
$word = new Word($randomWord);
$climate->br()->out(
strtr(
'? ' . $LANG['looking'] . ':',
[
'%length%' => $wordLength,
]
)
)->br();
$emojiLines = [];
$answers = [];
$try = 0;
do {
$input = $climate->input(
strtr('? '. $LANG['try'] .' %try/%attempts:', [
'%try' => strval($try + 1),
'%attempts' => $attempts,
])
);
$ansTries = $input->prompt();
if (strlen($ansTries) === 0) {
$noMatch = true;
$climate->error(
sprintf($LANG['must'], $word->length())
);
continue;
}
$attemptUpper = mb_strtoupper($ansTries);
$attemptLower = mb_strtolower($ansTries);
$against = new Word($attemptUpper);
if ($against->length() !== $word->length()) {
$noMatch = true;
$climate->error(
sprintf($LANG['must'], $word->length())
);
continue;
}
if (! in_array($attemptLower, $wordList, true)) {
$noMatch = true;
$climate->error($LANG['unknown']);
continue;
}
$answers[] = $attemptUpper;
$compare = new Compare($word, $against);
$noMatch = ! $compare->match();
$explain = [];
$emoji = '';
foreach ($against->split() as $pos => $letter) {
$match = $compare->computed()[$pos][$letter];
$color = '' . match ($match) {
Word::CHAR_MATCH_EXACT => 'green',
Word::CHAR_MATCH_PARTIAL => 'light_yellow',
Word::CHAR_MATCH_NONE => 'dark_gray',
};
$explain[] = "<invert><{$color}> {$letter} </{$color}></invert>";
$emoji .= match ($match) {
Word::CHAR_MATCH_EXACT => '?',
Word::CHAR_MATCH_PARTIAL => '?',
Word::CHAR_MATCH_NONE => '?',
};
}
$emojiLines[] = $emoji;
$explain[] = ' <invert><green>(' . $compare->fraction() . ')</green></invert>';
$climate->out(implode('', $explain));
$try++;
} while ($noMatch && ($attempts !== '?' && $try < $attempts));
$climate->animation($noMatch ? 'lose' : 'win')->enterFrom('bottom');
$LANGdgame = strtr('%icon% GAME OVER (%tries% ' . $LANG['tries'] . ') ? %word%', [
'%tries%' => $try,
'%icon%' => $noMatch ? '?' : '?',
'%word%' => $randomWord,
]);
$climate->flank($LANGdgame, '*', 3);
$climate->br()->out(implode("\n", $emojiLines));
|