Move to src-php to make room for Go

This commit is contained in:
2023-12-03 21:00:03 +01:00
parent 49a526bce2
commit d8d3821113
14 changed files with 5 additions and 5 deletions

View File

@ -0,0 +1,89 @@
<?php
namespace trizz\AdventOfCode\Commands;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use trizz\AdventOfCode\Solution;
abstract class AbstractDayCommand extends Command
{
protected int $day;
protected int $year;
protected string $title;
protected bool $skipExamples;
protected int $part;
#[\Override]
protected function configure(): void
{
$this
->addArgument('day', InputArgument::REQUIRED, 'The day number')
->addArgument('year', InputArgument::OPTIONAL, 'The year', date('y'))
->addOption('one', '1', null, 'Run only part 1')
->addOption('two', '2', null, 'Run only part 2')
->addOption('skip-example', 's', null, 'Skip the example data');
}
#[\Override]
protected function initialize(InputInterface $input, OutputInterface $output): void
{
$this->day = $input->getArgument('day');
$this->year = $input->getArgument('year');
$this->title = sprintf("Advent of Code '%d - Day %d", $this->year, $this->day);
$this->skipExamples = $input->getOption('skip-example');
$this->part = $input->getOption('one') ? 1 : ($input->getOption('two') ? 2 : 0);
$output->writeln('');
$output->writeln($this->title);
$output->writeln(str_repeat('-', strlen($this->title)));
}
/**
* @return array<string, int|string>
*/
protected function getSolutions(): array
{
$solution = $this->loadClass();
// Solve the examples if available.
$resultPart1Example = 'n/a';
$resultPart2Example = 'n/a';
if (!$this->skipExamples && $solution->hasExampleData()) {
['part1' => $resultPart1Example, 'part2' => $resultPart2Example] = $solution->results(useExampleData: true, part: $this->part);
}
// Solve the real puzzle if available.
$resultPart1 = 'n/a';
$resultPart2 = 'n/a';
if ($solution->hasData()) {
['part1' => $resultPart1, 'part2' => $resultPart2] = $solution->results(useExampleData: false, part: $this->part);
}
return [
'part1' => $resultPart1,
'part2' => $resultPart2,
'part1Example' => $resultPart1Example,
'part2Example' => $resultPart2Example,
];
}
protected function loadClass(): Solution
{
require_once sprintf('%s/Y%d/day%d/Day%d.php', DATA_DIR, $this->year, $this->day, $this->day);
$className = sprintf('%s\\Y%d\\Day%d', substr(__NAMESPACE__, 0, -9), $this->year, $this->day);
/** @var Solution $class */
$class = new $className();
$class->loadData();
return $class;
}
}

135
src-php/Commands/AddDay.php Normal file
View File

@ -0,0 +1,135 @@
<?php
namespace trizz\AdventOfCode\Commands;
use Nette\PhpGenerator\ClassType;
use Nette\PhpGenerator\PhpNamespace;
use Nette\PhpGenerator\Printer;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use trizz\AdventOfCode\Solution;
use function Laravel\Prompts\text;
final class AddDay extends Command
{
private string $dataDir;
#[\Override]
protected function configure(): void
{
$this
->setName('new')
->setDescription('Add a new day.')
->addArgument('day', InputArgument::OPTIONAL, 'The day number.')
->addArgument('year', InputArgument::OPTIONAL, 'The year', date('y'));
}
#[\Override]
protected function execute(InputInterface $input, OutputInterface $output): int
{
$day = $input->getArgument('day');
$year = $input->getArgument('year');
if ($day === null) {
$day = text(
label: 'For which day?',
placeholder: '3',
default: date('j'),
required: true,
validate: static fn ($value): ?string => is_numeric($value) && $value > 0 && $value < 26 ? null : 'Please enter a valid day number.'
);
}
$example1Result = text(
label: 'Please enter the result for example 1.',
required: true,
validate: static fn ($value): ?string => is_numeric($value) ? null : 'Please enter a valid number.'
);
// Create short year.
$year = strlen((string) $year) === 4 ? substr((string) $year, 2) : $year;
$this->dataDir = DATA_DIR.'/Y'.$year.'/day'.$day;
$output->writeln(sprintf("Adding files for day %s of year '%s.", $day, $year));
$this
->addDirsAndExampleFiles($year, $day)
->createClass($day, $year, $example1Result);
return Command::SUCCESS;
}
private function createClass(string $day, string $year, string $example1Result): self
{
$phpNamespace = new PhpNamespace('trizz\AdventOfCode\Y'.$year);
$classType = new ClassType('Day'.$day);
$classType->setFinal()->setExtends(Solution::class);
$this->createClassProperties($classType, $example1Result);
$this->createClassMethods($classType);
$phpNamespace->add($classType);
$this->printClassToFile($classType, $year, $day, $phpNamespace);
return $this;
}
private function addDirsAndExampleFiles(mixed $year, mixed $day): self
{
if (!is_dir($this->dataDir) && !mkdir($this->dataDir, recursive: true) && !is_dir($this->dataDir)) {
throw new \RuntimeException(sprintf('Directory "%s" was not created', $this->dataDir));
}
touch($this->dataDir.'/example.txt');
touch($this->dataDir.'/data.txt');
return $this;
}
private function createClassProperties(ClassType $classType, string $example1Result): void
{
$properties = [
'part1ExampleResult' => $example1Result,
'part1Result' => null,
'part2ExampleResult' => null,
'part2Result' => null,
];
foreach ($properties as $name => $value) {
$classType
->addProperty($name, $value)
->setPublic()
->setStatic()
->setType('null|int|string');
}
}
private function createClassMethods(ClassType $classType): void
{
$methods = ['part1', 'part2'];
foreach ($methods as $method) {
$classType
->addMethod($method)
->setReturnType('int')
->setBody('return -1;')
->addParameter('data')->setType('array');
}
}
private function printClassToFile(ClassType $classType, string $year, string $day, PhpNamespace $phpNamespace): void
{
$printer = new Printer();
$printer->indentation = ' ';
$filename = $this->dataDir.'/Day'.$day.'.php';
if (file_exists($filename)) {
throw new \RuntimeException(sprintf('File %s already exists.', $filename));
}
file_put_contents($filename, '<?php'.PHP_EOL.$printer->printNamespace($phpNamespace));
}
}

View File

@ -0,0 +1,44 @@
<?php
namespace trizz\AdventOfCode\Commands;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
final class ExecuteDay extends AbstractDayCommand
{
#[\Override]
protected function configure(): void
{
parent::configure();
$this
->setName('day')
->setDescription('Run day');
}
#[\Override]
protected function execute(InputInterface $input, OutputInterface $output): int
{
$results = $this->getSolutions();
// Output all the results.
if (in_array($this->part, [0, 1], true)) {
$output->writeln('<fg=bright-green>Part 1</>');
$output->writeln(sprintf(' <fg=blue>Example:</> <comment>%s</comment>', $results['part1Example']));
$output->writeln(sprintf(' <fg=blue>Result: </> <comment>%s</comment>', $results['part1']));
}
if ($this->part === 0) {
$output->writeln(str_repeat('-', strlen($this->title)));
}
if (in_array($this->part, [0, 2], true)) {
$output->writeln('<fg=bright-green>Part 2</>');
$output->writeln(sprintf(' <fg=blue>Example:</> <comment>%s</comment>', $results['part2Example']));
$output->writeln(sprintf(' <fg=blue>Result: </> <comment>%s</comment>', $results['part2']));
}
return Command::SUCCESS;
}
}

View File

@ -0,0 +1,47 @@
<?php
namespace trizz\AdventOfCode\Commands;
use PhpPkg\CliMarkdown\CliMarkdown;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
final class Puzzle extends Command
{
#[\Override]
protected function configure(): void
{
$this
->setName('puzzle')
->setDescription('Show the puzzle description.')
->addArgument('day', InputArgument::REQUIRED, 'The day number.')
->addArgument('year', InputArgument::OPTIONAL, 'The year', date('y'));
}
#[\Override]
protected function execute(InputInterface $input, OutputInterface $output): int
{
$contents = file_get_contents(
sprintf(
'%s/../../data/Y%d/day%s/puzzle.md',
__DIR__,
$input->getArgument('year'),
(int) $input->getArgument('day')
)
);
if (!$contents) {
$output->writeln('Can not read puzzle.');
return Command::FAILURE;
}
$rendered = (new CliMarkdown())->render($contents);
$output->writeln($rendered);
return Command::SUCCESS;
}
}

View File

@ -0,0 +1,56 @@
<?php
namespace trizz\AdventOfCode\Commands;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
final class TestDay extends AbstractDayCommand
{
#[\Override]
protected function configure(): void
{
parent::configure();
$this
->setName('test')
->setDescription('Test day');
}
#[\Override]
protected function execute(InputInterface $input, OutputInterface $output): int
{
$results = $this->getSolutions();
$solution = $this->loadClass();
if (in_array($this->part, [0, 1], true)) {
$output->writeln('<fg=bright-green>Part 1</>');
$this->render($output, 'Example', $solution::$part1ExampleResult, $results['part1Example']);
$this->render($output, 'Puzzle', $solution::$part1Result, $results['part1']);
}
if ($this->part === 0) {
$output->writeln(str_repeat('-', strlen($this->title)));
}
if (in_array($this->part, [0, 2], true)) {
$output->writeln('<fg=bright-green>Part 2</>');
$this->render($output, 'Example', $solution::$part2ExampleResult, $results['part2Example']);
$this->render($output, 'Puzzle', $solution::$part2Result, $results['part2']);
}
return Command::SUCCESS;
}
private function render(OutputInterface $output, string $title, null|int|string $expected, null|int|string $result): void
{
if ($title === 'Example' && $this->skipExamples) {
return;
}
$color = $expected === $result ? 'green' : 'red';
$output->writeln(sprintf(' <fg=blue>%s:</>', $title));
$output->writeln(sprintf(' <fg=%s>Expected: </> <comment>%s</comment>', $color, $expected));
$output->writeln(sprintf(' <fg=%s>Result: </> <comment>%s</comment>', $color, $result));
}
}