2019-11-02 20:19:34 +01:00
|
|
|
<?php
|
|
|
|
|
2020-01-19 13:16:40 +01:00
|
|
|
class AdventOfCode implements IWebsiteModule
|
2019-11-02 20:19:34 +01:00
|
|
|
{
|
|
|
|
const YEARS =
|
|
|
|
[
|
2020-12-02 08:32:04 +01:00
|
|
|
'2017' => [ 'url-aoc'=>'https://adventofcode.com/2017/day/', 'blog-id' => 25, 'github' => 'https://github.com/Mikescher/AdventOfCode2017', 'single_solution_file' => false ],
|
|
|
|
'2018' => [ 'url-aoc'=>'https://adventofcode.com/2018/day/', 'blog-id' => 23, 'github' => 'https://github.com/Mikescher/AdventOfCode2018', 'single_solution_file' => false ],
|
|
|
|
'2019' => [ 'url-aoc'=>'https://adventofcode.com/2019/day/', 'blog-id' => 24, 'github' => 'https://github.com/Mikescher/AdventOfCode2019', 'single_solution_file' => false ],
|
|
|
|
'2020' => [ 'url-aoc'=>'https://adventofcode.com/2020/day/', 'blog-id' => 26, 'github' => 'https://github.com/Mikescher/AdventOfCode2020', 'single_solution_file' => true ],
|
2021-12-01 12:00:03 +01:00
|
|
|
'2021' => [ 'url-aoc'=>'https://adventofcode.com/2021/day/', 'blog-id' => 27, 'github' => 'https://github.com/Mikescher/AdventOfCode2021', 'single_solution_file' => true ],
|
2019-11-02 20:19:34 +01:00
|
|
|
];
|
|
|
|
|
|
|
|
const LANGUAGES =
|
|
|
|
[
|
2019-11-03 00:06:25 +01:00
|
|
|
'cs' => ['ext'=>'linq', 'css'=>'language-csharp', 'name'=>'C#'],
|
|
|
|
'java' => ['ext'=>'java', 'css'=>'language-java', 'name'=>'Java'],
|
|
|
|
'bef' => ['ext'=>'b93', 'css'=>'language-befungerunner', 'name'=>'Befunge-93+'],
|
|
|
|
'cpp' => ['ext'=>'cpp', 'css'=>'language-cpp', 'name'=>'C++'],
|
|
|
|
'pyth' => ['ext'=>'py', 'css'=>'language-python', 'name'=>'Python'],
|
|
|
|
'rust' => ['ext'=>'rs', 'css'=>'language-rust', 'name'=>'Rust'],
|
|
|
|
'go' => ['ext'=>'go', 'css'=>'language-go', 'name'=>'Go'],
|
|
|
|
'js' => ['ext'=>'js', 'css'=>'language-javascript', 'name'=>'Javascript'],
|
|
|
|
'pas' => ['ext'=>'pas', 'css'=>'language-pascal', 'name'=>'Pascal/Delphi'],
|
2019-11-08 16:09:42 +01:00
|
|
|
'ts' => ['ext'=>'ts', 'css'=>'language-typescript', 'name'=>'Typescript'],
|
2019-11-02 20:19:34 +01:00
|
|
|
];
|
|
|
|
|
2020-01-15 00:04:40 +01:00
|
|
|
/** @var array */
|
|
|
|
private $staticData;
|
|
|
|
|
|
|
|
public function __construct()
|
|
|
|
{
|
|
|
|
$this->load();
|
|
|
|
}
|
|
|
|
|
|
|
|
private function load()
|
2019-11-02 20:19:34 +01:00
|
|
|
{
|
2020-01-15 02:50:23 +01:00
|
|
|
$all = require (__DIR__ . '/../../statics/aoc/__all.php');
|
2019-11-02 20:19:34 +01:00
|
|
|
|
|
|
|
array_walk($all, function(&$value, $year) { array_walk($value, function (&$innervalue) use ($year) { $innervalue = self::readSingle($year, $innervalue); }); });
|
|
|
|
|
2020-01-15 00:04:40 +01:00
|
|
|
$this->staticData = $all;
|
2019-11-02 20:19:34 +01:00
|
|
|
}
|
|
|
|
|
2020-01-15 00:04:40 +01:00
|
|
|
public function listAllFromAllYears()
|
2019-11-02 20:19:34 +01:00
|
|
|
{
|
2020-01-15 00:04:40 +01:00
|
|
|
return $this->staticData;
|
|
|
|
}
|
2019-11-03 00:06:25 +01:00
|
|
|
|
2020-01-19 01:22:06 +01:00
|
|
|
public function listAllDays()
|
|
|
|
{
|
|
|
|
$r = [];
|
2020-01-20 18:28:31 +01:00
|
|
|
foreach ($this->staticData as $year => $yeardata) foreach ($yeardata as $daydata) $r []= $daydata;
|
2020-01-20 18:36:33 +01:00
|
|
|
return $r;
|
2020-01-19 01:22:06 +01:00
|
|
|
}
|
|
|
|
|
2020-01-15 00:04:40 +01:00
|
|
|
public function listSingleYear($year)
|
|
|
|
{
|
|
|
|
return $this->staticData[$year];
|
2019-11-03 00:06:25 +01:00
|
|
|
}
|
|
|
|
|
2020-01-15 00:04:40 +01:00
|
|
|
public function listSingleYearAssociative($year)
|
2019-11-03 00:06:25 +01:00
|
|
|
{
|
2020-01-15 00:04:40 +01:00
|
|
|
$all = $this->listSingleYear($year);
|
2019-11-03 00:06:25 +01:00
|
|
|
|
|
|
|
$result = array_fill(0, 25, null);
|
|
|
|
|
|
|
|
foreach ($all as $d)
|
|
|
|
{
|
2020-01-15 00:04:40 +01:00
|
|
|
$result[$d['day'] - 1] = $d;
|
2019-11-03 00:06:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return $result;
|
2019-11-02 20:19:34 +01:00
|
|
|
}
|
|
|
|
|
2020-01-15 00:04:40 +01:00
|
|
|
public function listYears()
|
2019-11-02 20:19:34 +01:00
|
|
|
{
|
2020-01-15 00:04:40 +01:00
|
|
|
return array_keys($this->staticData);
|
2019-11-02 20:19:34 +01:00
|
|
|
}
|
|
|
|
|
2020-01-15 00:32:24 +01:00
|
|
|
private static function readSingle($year, $a)
|
2019-11-02 20:19:34 +01:00
|
|
|
{
|
|
|
|
$yeardata = self::YEARS[$year];
|
|
|
|
|
2020-12-02 08:32:04 +01:00
|
|
|
$a['single_solution_file'] = $yeardata['single_solution_file'];
|
|
|
|
|
2019-11-02 20:19:34 +01:00
|
|
|
$n2p = str_pad($a['day'], 2, '0', STR_PAD_LEFT);
|
|
|
|
$a['day-padded'] = $n2p;
|
|
|
|
|
2020-01-20 21:13:26 +01:00
|
|
|
$a['url'] = '/blog/' . $yeardata['blog-id'] . '/Advent_of_Code_' . $year . '/day-' . $n2p;
|
|
|
|
$a['url-alternative'] = '/adventofcode/' . $year . '/' . $n2p;
|
2019-11-02 20:19:34 +01:00
|
|
|
$a['canonical'] = "https://www.mikescher.com" . $a['url'];
|
|
|
|
|
|
|
|
$a['url_aoc'] = $yeardata['url-aoc'] . $a['day']; // adventofcode.com/{year}/day/{day}
|
|
|
|
|
2020-01-17 00:36:37 +01:00
|
|
|
$a['file_challenge'] = (__DIR__ . '/../../statics/aoc/'.$year.'/'.$n2p.'_challenge.txt');
|
|
|
|
$a['file_input'] = (__DIR__ . '/../../statics/aoc/'.$year.'/'.$n2p.'_input.txt');
|
2019-11-02 20:19:34 +01:00
|
|
|
|
2020-01-19 01:22:06 +01:00
|
|
|
$a['year'] = $year;
|
2019-11-03 15:33:23 +01:00
|
|
|
$a['date'] = $year . '-' . 12 . '-' . $n2p;
|
|
|
|
|
2019-11-02 20:19:34 +01:00
|
|
|
$solutionfiles = [];
|
|
|
|
|
2020-12-02 08:32:04 +01:00
|
|
|
if ($a['single_solution_file'])
|
2019-11-02 20:19:34 +01:00
|
|
|
{
|
2020-12-02 08:32:04 +01:00
|
|
|
$solutionfiles []= (__DIR__ . '/../../statics/aoc/' . $year . '/' . $n2p . '_solution' . '.' . self::LANGUAGES[$a['language']]['ext']);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for ($i=1; $i <= $a['parts']; $i++)
|
|
|
|
{
|
|
|
|
$solutionfiles []= (__DIR__ . '/../../statics/aoc/' . $year . '/' . $n2p . '_solution-' . $i . '.' . self::LANGUAGES[$a['language']]['ext']);
|
|
|
|
}
|
2019-11-02 20:19:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
$a['file_solutions'] = $solutionfiles;
|
|
|
|
|
|
|
|
return $a;
|
|
|
|
}
|
|
|
|
|
2020-01-15 00:04:40 +01:00
|
|
|
public function getDayFromStrIdent($year, $ident)
|
2019-11-02 20:19:34 +01:00
|
|
|
{
|
|
|
|
$e = explode('-', $ident, 2); // day-xxx
|
|
|
|
if (count($e)!==2) return null;
|
|
|
|
|
|
|
|
$i = intval($e[1], 10);
|
|
|
|
if ($i == 0) return null;
|
|
|
|
|
|
|
|
return self::getSingleDay($year, $i);
|
|
|
|
}
|
|
|
|
|
2020-01-15 00:04:40 +01:00
|
|
|
public function getSingleDay($year, $day)
|
2019-11-02 20:19:34 +01:00
|
|
|
{
|
2020-01-15 00:04:40 +01:00
|
|
|
foreach ($this->listSingleYear($year) as $aocd) {
|
2019-11-02 20:19:34 +01:00
|
|
|
if ($aocd['day'] == $day) return $aocd;
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2020-01-15 00:04:40 +01:00
|
|
|
public function getGithubLink($year)
|
2019-11-03 00:06:25 +01:00
|
|
|
{
|
|
|
|
return self::YEARS['' . $year]['github'];
|
|
|
|
}
|
|
|
|
|
2020-01-15 00:04:40 +01:00
|
|
|
public function getURLForYear($year)
|
2019-11-03 00:06:25 +01:00
|
|
|
{
|
|
|
|
return '/blog/' . self::YEARS[''.$year]['blog-id'] . '/Advent_of_Code_' . $year . '/';
|
|
|
|
}
|
|
|
|
|
2020-01-15 00:04:40 +01:00
|
|
|
public function getPrevYear($year)
|
2019-11-03 00:06:25 +01:00
|
|
|
{
|
|
|
|
$last = null;
|
|
|
|
foreach (self::YEARS as $y => $d)
|
|
|
|
{
|
|
|
|
if ($y == $year) return $last;
|
|
|
|
$last = $y;
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2020-01-15 00:04:40 +01:00
|
|
|
public function getNextYear($year)
|
2019-11-03 00:06:25 +01:00
|
|
|
{
|
|
|
|
$found = false;
|
|
|
|
foreach (self::YEARS as $y => $d)
|
|
|
|
{
|
|
|
|
if ($found) return $y;
|
|
|
|
if ($y == $year) $found = true;
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2020-01-15 00:04:40 +01:00
|
|
|
public function getLanguageCSS($data)
|
2019-11-03 15:33:23 +01:00
|
|
|
{
|
|
|
|
return self::LANGUAGES[$data['language']]['css'];
|
|
|
|
}
|
|
|
|
|
2020-01-15 00:04:40 +01:00
|
|
|
public function getSolutionCode($data, $i)
|
2019-11-03 15:33:23 +01:00
|
|
|
{
|
|
|
|
$raw = file_get_contents($data['file_solutions'][$i]);
|
|
|
|
|
|
|
|
if ($data['language'] === 'cs')
|
|
|
|
{
|
|
|
|
$raw = trim($raw);
|
|
|
|
if (startsWith($raw, '<Query Kind="Program" />')) $raw = substr($raw, strlen('<Query Kind="Program" />'));
|
|
|
|
if (startsWith($raw, '<Query Kind="Expression" />')) $raw = substr($raw, strlen('<Query Kind="Expression" />'));
|
|
|
|
if (startsWith($raw, '<Query Kind="Statements" />')) $raw = substr($raw, strlen('<Query Kind="Statements" />'));
|
|
|
|
$raw = trim($raw);
|
|
|
|
}
|
|
|
|
|
|
|
|
return $raw;
|
|
|
|
}
|
|
|
|
|
2020-01-15 00:04:40 +01:00
|
|
|
public function checkConsistency()
|
2019-11-02 20:19:34 +01:00
|
|
|
{
|
|
|
|
$warn = null;
|
|
|
|
|
2020-01-15 00:04:40 +01:00
|
|
|
$this->load();
|
|
|
|
|
|
|
|
foreach ($this->listAllFromAllYears() as $year => $yd)
|
2019-11-02 20:19:34 +01:00
|
|
|
{
|
|
|
|
$daylist = [];
|
|
|
|
$titlelist = [];
|
|
|
|
|
|
|
|
if (!array_key_exists($year, self::YEARS)) return ['result'=>'err', 'message' => 'Invalid Year: ' . $year];
|
|
|
|
|
|
|
|
foreach ($yd as $aocdata)
|
|
|
|
{
|
|
|
|
if (in_array($aocdata['day'], $daylist)) return ['result'=>'err', 'message' => 'Duplicate day ' . $aocdata['day']];
|
|
|
|
$daylist []= $aocdata['day'];
|
|
|
|
|
|
|
|
if (in_array($aocdata['title'], $titlelist)) return ['result'=>'err', 'message' => 'Duplicate title ' . $aocdata['title']];
|
|
|
|
$titlelist []= $aocdata['title'];
|
|
|
|
|
2020-12-02 08:32:04 +01:00
|
|
|
if ($aocdata['day'] < 1 || $aocdata['day'] > 25) return ['result'=>'err', 'message' => 'Invalid [day]-value title ' . $aocdata['day']];
|
2019-11-03 00:06:25 +01:00
|
|
|
|
2019-11-02 20:19:34 +01:00
|
|
|
if (count($aocdata['solutions']) !== $aocdata['parts']) return ['result'=>'err', 'message' => 'Not enough solution-values in day' . $aocdata['day']];
|
2020-12-02 08:32:04 +01:00
|
|
|
|
|
|
|
if ($aocdata['single_solution_file']) {
|
|
|
|
if (count($aocdata['file_solutions']) !== 1) return ['result'=>'err', 'message' => 'Not enough solution-files in day' . $aocdata['day']];
|
|
|
|
} else {
|
|
|
|
if (count($aocdata['file_solutions']) !== $aocdata['parts']) return ['result'=>'err', 'message' => 'Not enough solution-files in day' . $aocdata['day']];
|
|
|
|
}
|
2019-11-02 20:19:34 +01:00
|
|
|
|
|
|
|
if (!file_exists($aocdata['file_challenge'])) return ['result'=>'err', 'message' => 'file_challenge not found ' . $aocdata['file_challenge']];
|
|
|
|
if (!file_exists($aocdata['file_input'])) return ['result'=>'err', 'message' => 'file_input not found ' . $aocdata['file_input']];
|
|
|
|
|
|
|
|
foreach ($aocdata['file_solutions'] as $sfile)
|
|
|
|
{
|
|
|
|
if (!file_exists($sfile)) return ['result'=>'err', 'message' => 'file_solution[?] not found ' . $sfile];
|
|
|
|
}
|
|
|
|
|
2019-11-03 15:33:23 +01:00
|
|
|
if (!array_key_exists($aocdata['language'], self::LANGUAGES)) return ['result'=>'err', 'message' => 'Unknown language ' . $aocdata['language']];
|
2019-11-02 20:19:34 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($warn != null) return $warn;
|
|
|
|
return ['result'=>'ok', 'message' => ''];
|
|
|
|
}
|
|
|
|
}
|