// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; /// @title Ledger — the judge of Genesis Run /// @notice A 31 x 31 maze through twelve blocks of crypto history, 2008 to 2024. /// A run is a list of moves, one byte each: 0 up, 1 right, 2 down, 3 left. /// The contract walks the run cell by cell and records it only if it /// never touches a wall, clears all twelve stations in order and ends on the exit. /// There is no owner, no setter and nothing to withdraw. contract Ledger { uint256 public constant SIZE = 31; uint256 public constant MAX_MOVES = 6000; uint256 public constant START = 32; // (1,1) uint256 public constant EXIT = 928; // (29,29) /// The maze, 961 cells row by row, one bit per cell, 1 is a wall. /// Cell c sits at bit (c % 256) of word (c / 256). uint256 internal constant WALL0 = 0xfd8a082223d77f5556a288aaad7757577a8888a23577fd7762000202ffffffff; uint256 internal constant WALL1 = 0x5d5da00a288b5ff7d7d622002a2df5ff55da0a028a35f5f7d7ea08a020d5dd7f; uint256 internal constant WALL2 = 0xd575fd888a2823fdf75f762222222d5577f77a2a28023775dddf6888a2a0df7d; uint256 internal constant WALL3 = 0x0000000000000001fffffffe0800820df7f75dd8282880b75d57ff628aa200f5; /// The same layout, 1 marks a station cell. uint256 internal constant MARK0 = 0x0000000000000000000000000000000000040000000000000000000000000000; uint256 internal constant MARK1 = 0x8000000000000000000000000040000020000000000008000000010000000000; uint256 internal constant MARK2 = 0x0000000000420000000000400000000000000000000000080000000000000000; uint256 internal constant MARK3 = 0x0000000000000000000000000000000000000000000008000000000010000000; /// Twelve station cells, 16 bits each, in order: /// 2008, 2009, 2010, 2012, 2014, 2015, 2016, 2017, 2020, 2021, 2022, 2024 uint256 internal constant STATIONS = 0x2a60243014b00720128017d01ff031c02d6034b02d10196; struct Run { address runner; uint32 moves; uint64 at; } mapping(address => uint32) public best; uint32 public record; address public recordHolder; uint32 public finishes; Run[] internal runs; event Cleared(address indexed runner, uint32 moves, bool personalBest, bool newRecord); /// @notice Walks a run without writing anything. Free to call. /// @return ok true if the run is valid and finished /// @return steps moves walked before the walk ended /// @return cleared stations cleared in order /// @return at cell index where the walk ended /// @return reason empty on success, otherwise why the floor refused function verify(bytes calldata moves) public pure returns (bool ok, uint256 steps, uint256 cleared, uint256 at, string memory reason) { at = START; if (moves.length == 0) return (false, 0, 0, at, "empty run"); if (moves.length > MAX_MOVES) return (false, 0, 0, at, "over 6000 moves"); uint256 x = 1; uint256 y = 1; for (uint256 i = 0; i < moves.length; i++) { uint8 m = uint8(moves[i]); if (m == 0) { if (y == 0) return (false, i, cleared, at, "off the grid"); y--; } else if (m == 1) { if (x + 1 >= SIZE) return (false, i, cleared, at, "off the grid"); x++; } else if (m == 2) { if (y + 1 >= SIZE) return (false, i, cleared, at, "off the grid"); y++; } else if (m == 3) { if (x == 0) return (false, i, cleared, at, "off the grid"); x--; } else return (false, i, cleared, at, "unknown move code"); uint256 c = y * SIZE + x; if (_bit(WALL0, WALL1, WALL2, WALL3, c)) return (false, i, cleared, at, "into a wall"); at = c; if (_bit(MARK0, MARK1, MARK2, MARK3, c)) { for (uint256 s = 0; s < 12; s++) { if (_station(s) == c) { if (s == cleared) cleared++; else if (s > cleared) return (false, i + 1, cleared, at, "station out of order"); break; } } } } steps = moves.length; if (cleared < 12) return (false, steps, cleared, at, "stations missing"); if (at != EXIT) return (false, steps, cleared, at, "not at the exit"); return (true, steps, cleared, at, ""); } /// @notice Verifies and records a run. Reverts with the reason if refused. function submit(bytes calldata moves) external { (bool ok, , , , string memory reason) = verify(moves); require(ok, reason); uint32 n = uint32(moves.length); bool pb = best[msg.sender] == 0 || n < best[msg.sender]; if (pb) best[msg.sender] = n; bool rec = record == 0 || n < record; if (rec) { record = n; recordHolder = msg.sender; } finishes++; runs.push(Run(msg.sender, n, uint64(block.timestamp))); emit Cleared(msg.sender, n, pb, rec); } /// @notice The most recent runs, newest first, up to fifty. function lastRuns() external view returns (Run[] memory out) { uint256 n = runs.length < 50 ? runs.length : 50; out = new Run[](n); for (uint256 i = 0; i < n; i++) out[i] = runs[runs.length - 1 - i]; } /// @notice The twelve station cells, in order. function stations() external pure returns (uint16[12] memory out) { for (uint256 s = 0; s < 12; s++) out[s] = uint16(_station(s)); } /// @notice True if the cell is a wall. function isWall(uint256 c) external pure returns (bool) { return c >= SIZE * SIZE || _bit(WALL0, WALL1, WALL2, WALL3, c); } function _station(uint256 s) internal pure returns (uint256) { return (STATIONS >> (16 * s)) & 0xffff; } function _bit(uint256 w0, uint256 w1, uint256 w2, uint256 w3, uint256 c) internal pure returns (bool) { uint256 w = c < 256 ? w0 : c < 512 ? w1 : c < 768 ? w2 : w3; return (w >> (c & 255)) & 1 == 1; } }