├── 2019 ├── 10 │ └── 10.py ├── 11 │ ├── 11.py │ └── computer.py ├── 12 │ └── 12.py ├── 13 │ ├── 13.py │ ├── 13b.py │ └── computer.py ├── 14 │ └── 14.py ├── 15 │ └── 15.py ├── 01 │ ├── 1-1.ts │ ├── 1-2.ts │ ├── input.txt │ ├── package-lock.json │ ├── package.json │ └── tsconfig.json ├── 02 │ ├── 2.py │ └── 2.txt ├── 03 │ ├── 3.php │ └── 3.txt ├── 04 │ └── 4.pl ├── 05 │ └── 5.py ├── 06 │ ├── 6.py │ └── 6b.py ├── 07 │ ├── 7.py │ ├── 7b.py │ └── computer.py ├── 08 │ └── 8.py └── 09 │ ├── 9.py │ └── computer.py ├── 2020 ├── 1 │ ├── 1.py │ └── 1b.py ├── 2 │ ├── 2.py │ └── 2b.py ├── 3 │ └── 3.py ├── 4 │ ├── 4.py │ └── 4b.py ├── 5 │ └── 5.py ├── 6 │ └── 6.py ├── 7 │ └── 7.py ├── 8 │ ├── 8.py │ └── 8b.py ├── 9 │ └── 9.py ├── 10 │ └── 10.py ├── 11 │ ├── 11.py │ └── 11b.py ├── 12 │ ├── 12.py │ └── 12b.py ├── 13 │ ├── 13.py │ └── 13b.py ├── 14 │ ├── 14.py │ └── 14b.py ├── 15 │ └── 15.py ├── 16 │ ├── 16.py │ └── 16b.py ├── 17 │ └── 17.py ├── 18 │ ├── 18.py │ └── 18b.py ├── 19 │ └── 19.py ├── 20 │ └── 20.py ├── 21 │ └── 21.py ├── 22 │ ├── 22.py │ └── 22b.py ├── 23 │ ├── 23.py │ └── 23b2.py ├── 24 │ └── 24.py └── 25 │ └── 25.py ├── 2021 ├── 1 │ ├── 1.py │ └── 1b.py ├── 2 │ ├── 2.py │ └── 2b.py ├── 3 │ ├── 3.py │ └── 3b.py ├── 4 │ ├── 4.py │ └── 4b.py ├── 5 │ ├── 5.py │ └── 5b.py ├── 6 │ ├── 6.py │ └── 6b.py ├── 7 │ ├── 7.py │ └── 7b.py ├── 8 │ ├── 8.py │ └── 8b.py ├── 9 │ ├── 9.py │ └── 9b.py ├── 10 │ ├── 10.py │ └── 10b.py ├── 11 │ ├── 11.py │ └── 11b.py ├── 12 │ ├── 12.py │ └── 12b.py ├── 13 │ ├── 13.py │ └── 13b.py ├── 14 │ ├── 14.py │ └── 14b.py ├── 15 │ ├── 15.py │ └── 15b.py ├── 16 │ ├── 16.py │ └── 16b.py ├── 17 │ ├── 17.py │ └── 17b.py ├── 18 │ ├── 18.py │ └── 18b.py ├── 20 │ ├── 20.py │ └── 20b.py ├── 21 │ ├── 21.py │ └── 21b.py └── 22 │ ├── 22.py │ └── 22b.py ├── 2022 ├── 1 │ ├── 1.py │ └── 1b.py ├── 2 │ ├── 2.py │ └── 2b.py ├── 3 │ ├── 3.py │ └── 3b.py ├── 4 │ ├── 4.py │ └── 4b.py ├── 5 │ ├── 5.py │ └── 5b.py ├── 6 │ ├── 6.py │ └── 6b.py ├── 7 │ ├── 7.py │ └── 7b.py ├── 8 │ ├── 8.py │ └── 8b.py ├── 9 │ ├── 9.py │ └── 9b.py ├── 11 │ ├── 11.py │ └── 11b.py ├── 12 │ ├── 12.py │ └── 12b.py └── 13 │ ├── 13.py │ └── 13b.py └── 2024 ├── 10 ├── 10a.py └── 10b.py ├── 11 ├── 11a.py └── 11b.py ├── 12 ├── 12a.py └── 12b.py ├── 13 ├── 13a.py └── 13b.py ├── 14 ├── 14a.py └── 14b.py ├── 15 ├── 15a.py └── 15b.py ├── 16 ├── 16a.py └── 16b.py ├── 18 ├── 18a.py └── 18b.py ├── 23 ├── 23a.py └── 23b.py ├── .gitignore ├── 01 ├── 01a.py └── 01b.py ├── 02 ├── 02a.py └── 02b.py ├── 03 ├── 03a.py └── 03b.py ├── 04 ├── 04a.py └── 04b.py ├── 05 ├── 05a.py └── 05b.py ├── 06 ├── 06a.py └── 06b.py ├── 07 ├── 07a.py └── 07b.py ├── 08 ├── 08a.py └── 08b.py └── 09 ├── 09a.py └── 09b.py /2019/01/1-1.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "promisified-fs" 2 | import * as R from "ramda" 3 | 4 | async function input() { 5 | let lines = 6 | (await fs.readFile('input.txt')).toString('ascii') 7 | .split('\n').slice(0, -1) 8 | return lines.map(x => parseInt(x, 10)) 9 | } 10 | function solve(modules) { 11 | return R.sum(modules.map(mass => Math.floor(mass / 3) - 2)) 12 | } 13 | 14 | (async () => { 15 | console.log(solve(await input())) 16 | })() 17 | -------------------------------------------------------------------------------- /2019/01/1-2.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "promisified-fs" 2 | import * as R from "ramda" 3 | 4 | async function input() { 5 | let lines = 6 | (await fs.readFile('input.txt')).toString('ascii') 7 | .split('\n').slice(0, -1) 8 | return lines.map((x: string) => parseInt(x, 10)) 9 | } 10 | 11 | function fuelRequiredForModule(mass: number) { 12 | function addExtraFuel(masses: number[]): number[] { 13 | return R.append(R.max(0, Math.floor(R.last(masses) / 3) - 2), masses) 14 | } 15 | 16 | let seq = 17 | R.until( 18 | R.compose(R.equals(0), R.last), 19 | addExtraFuel, 20 | [mass] 21 | ) 22 | 23 | return R.sum(R.tail(seq)) 24 | } 25 | 26 | function solve(modules: number[]) { 27 | return R.sum(modules.map(fuelRequiredForModule)) 28 | } 29 | 30 | (async () => { 31 | console.log(solve(await input())) 32 | })() 33 | -------------------------------------------------------------------------------- /2019/01/input.txt: -------------------------------------------------------------------------------- 1 | 80590 2 | 86055 3 | 92321 4 | 131464 5 | 73326 6 | 144607 7 | 124438 8 | 72589 9 | 96471 10 | 65712 11 | 107909 12 | 141197 13 | 131589 14 | 149356 15 | 53254 16 | 54742 17 | 94498 18 | 79631 19 | 146271 20 | 72983 21 | 59687 22 | 50571 23 | 89527 24 | 72175 25 | 72089 26 | 57808 27 | 143395 28 | 74329 29 | 109760 30 | 91254 31 | 79220 32 | 131610 33 | 74277 34 | 144080 35 | 107992 36 | 93817 37 | 112252 38 | 81157 39 | 74618 40 | 55479 41 | 66420 42 | 50055 43 | 53864 44 | 75143 45 | 131285 46 | 135352 47 | 63103 48 | 133893 49 | 142154 50 | 144706 51 | 128280 52 | 92891 53 | 61066 54 | 116696 55 | 132323 56 | 74805 57 | 75160 58 | 76285 59 | 114280 60 | 124461 61 | 86605 62 | 55868 63 | 117886 64 | 57035 65 | 125382 66 | 96755 67 | 50218 68 | 123795 69 | 141878 70 | 147718 71 | 65396 72 | 76043 73 | 53013 74 | 60583 75 | 140754 76 | 86844 77 | 99086 78 | 125917 79 | 139895 80 | 60719 81 | 76850 82 | 99552 83 | 130115 84 | 76143 85 | 113743 86 | 99243 87 | 132678 88 | 130983 89 | 137577 90 | 133118 91 | 70662 92 | 102478 93 | 132083 94 | 92287 95 | 147977 96 | 60584 97 | 91031 98 | 59910 99 | 147595 100 | 145263 101 | -------------------------------------------------------------------------------- /2019/01/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "1", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@types/node": { 8 | "version": "12.12.14", 9 | "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.14.tgz", 10 | "integrity": "sha512-u/SJDyXwuihpwjXy7hOOghagLEV1KdAST6syfnOk6QZAMzZuWZqXy5aYYZbh8Jdpd4escVFP0MvftHNDb9pruA==", 11 | "dev": true 12 | }, 13 | "@types/ramda": { 14 | "version": "0.26.36", 15 | "resolved": "https://registry.npmjs.org/@types/ramda/-/ramda-0.26.36.tgz", 16 | "integrity": "sha512-uo22Nf32Cc3EURp9tYlFttY8MVQZ1zoVum+D+yQ2jm4IqQFxDtwCQlP/WMwbuj4ey5kw1rJ7v6OacDszfWiqLA==", 17 | "dev": true, 18 | "requires": { 19 | "ts-toolbelt": "^4.10.7" 20 | } 21 | }, 22 | "promisified-fs": { 23 | "version": "1.0.1", 24 | "resolved": "https://registry.npmjs.org/promisified-fs/-/promisified-fs-1.0.1.tgz", 25 | "integrity": "sha512-qqvsT9GMZW+aDBjCzOWri44gIA29gKOXErsukWcR97ZxA/ODy2RmE4iZB95bD8IscneYSg7mRmdfH/iBi32BuQ==" 26 | }, 27 | "ramda": { 28 | "version": "0.26.1", 29 | "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.26.1.tgz", 30 | "integrity": "sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ==" 31 | }, 32 | "ts-toolbelt": { 33 | "version": "4.12.5", 34 | "resolved": "https://registry.npmjs.org/ts-toolbelt/-/ts-toolbelt-4.12.5.tgz", 35 | "integrity": "sha512-LxyONMKpDNPYtO3s63lQ7YNw93Xpsf6Krrvx/5XHT2I9YqgSZ0C+fERQKVklRgXOK+QhdDYplRvgdWPt43Eo0A==", 36 | "dev": true 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /2019/01/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "1", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "promisified-fs": "^1.0.1", 13 | "ramda": "^0.26.1" 14 | }, 15 | "devDependencies": { 16 | "@types/node": "^12.12.14", 17 | "@types/ramda": "^0.26.36" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /2019/01/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "commonjs", 5 | "lib": ["es2016"] 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /2019/02/2.py: -------------------------------------------------------------------------------- 1 | OP_ADD = 1 2 | OP_MUL = 2 3 | OP_HALT = 99 4 | 5 | with open("2.txt") as f: 6 | program = f.read() 7 | program = list(map(int, program.split(','))) 8 | 9 | def run(program, input): 10 | memory = {} 11 | for i, instruction in enumerate(program): 12 | memory[i] = instruction 13 | 14 | memory[1] = input[0] 15 | memory[2] = input[1] 16 | 17 | pc = 0 18 | while pc < len(program): 19 | opcode = memory[pc] 20 | if opcode == OP_HALT: 21 | break 22 | lop = memory[pc + 1] 23 | rop = memory[pc + 2] 24 | target = memory[pc + 3] 25 | lval = memory[lop] 26 | rval = memory[rop] 27 | if opcode == OP_ADD: 28 | res = lval + rval 29 | elif opcode == OP_MUL: 30 | res = lval * rval 31 | memory[target] = res 32 | pc += 4 33 | 34 | return memory[0] 35 | 36 | print(run(program, [12, 2])) 37 | 38 | for noun in range(100): 39 | for verb in range(100): 40 | if run(program, [noun, verb]) == 19690720: 41 | print(100 * noun + verb) 42 | -------------------------------------------------------------------------------- /2019/02/2.txt: -------------------------------------------------------------------------------- 1 | 1,0,0,3,1,1,2,3,1,3,4,3,1,5,0,3,2,10,1,19,1,19,9,23,1,23,6,27,1,9,27,31,1,31,10,35,2,13,35,39,1,39,10,43,1,43,9,47,1,47,13,51,1,51,13,55,2,55,6,59,1,59,5,63,2,10,63,67,1,67,9,71,1,71,13,75,1,6,75,79,1,10,79,83,2,9,83,87,1,87,5,91,2,91,9,95,1,6,95,99,1,99,5,103,2,103,10,107,1,107,6,111,2,9,111,115,2,9,115,119,2,13,119,123,1,123,9,127,1,5,127,131,1,131,2,135,1,135,6,0,99,2,0,14,0 2 | -------------------------------------------------------------------------------- /2019/03/3.php: -------------------------------------------------------------------------------- 1 | [ 1, 0], 15 | 'L' => [-1, 0], 16 | 'U' => [ 0, 1], 17 | 'D' => [ 0, -1]][$direction]; 18 | $end[0] = $start[0] + $amount * $delta[0]; 19 | $end[1] = $start[1] + $amount * $delta[1]; 20 | $orientation = ['L' => 0, 'R' => 0, 'U' => 1, 'D' => 1][$direction]; 21 | $position = $start[1 - $orientation]; 22 | 23 | return [ 24 | 'segment' => new Segment( 25 | $orientation, 26 | $position, 27 | $start[$orientation], 28 | $end[$orientation] 29 | ), 30 | 'end' => $end 31 | ]; 32 | } 33 | public function __construct($orientation, $position, $source, $destination) { 34 | if ($source < $destination) { 35 | $this->low = $source; 36 | $this->high = $destination; 37 | $this->direction = 1; 38 | } 39 | else { 40 | $this->low = $destination; 41 | $this->high = $source; 42 | $this->direction = -1; 43 | } 44 | assert(is_int($this->low) && is_int($this->high)); 45 | assert($this->low <= $this->high); 46 | $this->orientation = $orientation; 47 | $this->position = $position; 48 | } 49 | public function intersect($other) { 50 | if ($this->orientation == $other->orientation) { 51 | if ($this->position == $other->position) { 52 | $low = max($this->low, $other->low); 53 | $high = min($this->high, $other->high); 54 | if ($low <= $high) { 55 | return new Segment( 56 | $this->orientation, $this->position, $low, $high 57 | ); 58 | } 59 | } 60 | } 61 | else { 62 | if ($this->low <= $other->position && $other->position <= $this->high 63 | && $other->low <= $this->position && $this->position <= $other->high) { 64 | if ($this->orientation == 0) { 65 | return [$other->position, $this->position]; 66 | } 67 | return [$this->position, $other->position]; 68 | } 69 | } 70 | return NULL; 71 | } 72 | public function length() { 73 | return $this->high - $this->low; 74 | } 75 | public function pointLocation($point) { 76 | if ($point[1 - $this->orientation] != $this->position) { 77 | return NULL; 78 | } 79 | $location = $point[$this->orientation]; 80 | if ($location < $this->low || $location > $this->high) { 81 | return NULL; 82 | } 83 | if ($this->direction == 1) { 84 | return $location - $this->low; 85 | } 86 | else { 87 | return $this->high - $location; 88 | } 89 | } 90 | public function pointNearestOrigin() { 91 | if ($this->low <= 0 && $this->high >= 0) { 92 | $bestCoordinate = 0; 93 | } 94 | else { 95 | if ($this->low < 0) { 96 | $bestCoordinate = max($this->low, $this->high); 97 | } 98 | else { 99 | $bestCoordinate = min($this->low, $this->high); 100 | } 101 | } 102 | if ($this->orientation == 0) { 103 | return [$bestCoordinate, $this->position]; 104 | } 105 | return [$this->position, $bestCoordinate]; 106 | } 107 | } 108 | 109 | function manhattan($point) { 110 | return abs($point[0]) + abs($point[1]); 111 | } 112 | 113 | function segments($path) { 114 | $segments = []; 115 | $location = [0, 0]; 116 | foreach ($path as $instruction) { 117 | [ 118 | 'segment' => $segment, 119 | 'end' => $location 120 | ] = Segment::fromInstruction($location, $instruction); 121 | $segments[] = $segment; 122 | } 123 | return $segments; 124 | } 125 | 126 | $paths = file('3.txt'); 127 | $paths[0] = explode(',', $paths[0]); 128 | $paths[1] = explode(',', $paths[1]); 129 | 130 | $segments = [ 131 | segments($paths[0]), 132 | segments($paths[1]) 133 | ]; 134 | 135 | $bestDistance = INF; 136 | $bestDelay = INF; 137 | 138 | $delay0 = 0; 139 | foreach ($segments[0] as $segment0) { 140 | $delay1 = 0; 141 | foreach ($segments[1] as $segment1) { 142 | $candidate = $segment0->intersect($segment1); 143 | if ($candidate instanceof Segment) { 144 | $candidate = $candidate->pointNearestOrigin(); 145 | } 146 | if (!is_null($candidate)) { 147 | $candidateDistance = manhattan($candidate); 148 | if ($candidateDistance > 0) { 149 | $bestDistance = min($bestDistance, $candidateDistance); 150 | } 151 | $candidateDelay = $delay0 + $segment0->pointLocation($candidate) 152 | + $delay1 + $segment1->pointLocation($candidate); 153 | if ($candidateDelay > 0) { 154 | $bestDelay = min($bestDelay, $candidateDelay); 155 | } 156 | } 157 | $delay1 += $segment1->length(); 158 | } 159 | $delay0 += $segment0->length(); 160 | } 161 | 162 | echo "Best distance: $bestDistance\n"; 163 | echo "Best delay: $bestDelay\n"; 164 | ?> 165 | -------------------------------------------------------------------------------- /2019/03/3.txt: -------------------------------------------------------------------------------- 1 | R992,U284,L447,D597,R888,D327,R949,U520,R27,U555,L144,D284,R538,U249,R323,U297,R136,U838,L704,D621,R488,U856,R301,U539,L701,U363,R611,D94,L734,D560,L414,U890,R236,D699,L384,D452,R702,D637,L164,U410,R649,U901,L910,D595,R339,D346,R959,U777,R218,D667,R534,D762,R484,D914,L25,U959,R984,D922,R612,U999,L169,D599,L604,D357,L217,D327,L730,D949,L565,D332,L114,D512,R460,D495,L187,D697,R313,U319,L8,D915,L518,D513,R738,U9,R137,U542,L188,U440,R576,D307,R734,U58,R285,D401,R166,U156,L859,U132,L10,U753,L933,U915,R459,D50,R231,D166,L253,U844,R585,D871,L799,U53,R785,U336,R622,D108,R555,D918,L217,D668,L220,U738,L997,D998,R964,D456,L54,U930,R985,D244,L613,D116,L994,D20,R949,D245,L704,D564,L210,D13,R998,U951,L482,U579,L793,U680,L285,U770,L975,D54,R79,U613,L907,U467,L256,D783,R883,U810,R409,D508,L898,D286,L40,U741,L759,D549,R210,U411,R638,D643,L784,U538,L739,U771,L773,U491,L303,D425,L891,U182,R412,U951,L381,U501,R482,D625,R870,D320,L464,U555,R566,D781,L540,D754,L211,U73,L321,D869,R994,D177,R496,U383,R911,U819,L651,D774,L591,U666,L883,U767,R232,U822,L499,U44,L45,U873,L98,D487,L47,U803,R855,U256,R567,D88,R138,D678,L37,U38,R783,U569,L646,D261,L597,U275,L527,U48,R433,D324,L631,D160,L145,D128,R894,U223,R664,U510,R756,D700,R297,D361,R837,U996,L769,U813,L477,U420,L172,U482,R891,D379,L329,U55,R284,U155,L816,U659,L671,U996,R997,U252,R514,D718,L661,D625,R910,D960,L39,U610,R853,U859,R174,U215,L603,U745,L587,D736,R365,U78,R306,U158,L813,U885,R558,U631,L110,D232,L519,D366,R909,D10,R294 2 | L1001,D833,L855,D123,R36,U295,L319,D700,L164,U576,L68,D757,R192,D738,L640,D660,R940,D778,R888,U772,R771,U900,L188,D464,L572,U184,R889,D991,L961,U751,R560,D490,L887,D748,R37,U910,L424,D401,L385,U415,L929,U193,R710,D855,L596,D323,L966,D505,L422,D139,L108,D135,R737,U176,R538,D173,R21,D951,R949,D61,L343,U704,R127,U468,L240,D834,L858,D127,R328,D863,R329,U477,R131,U864,R997,D38,R418,U611,R28,U705,R148,D414,R786,U264,L785,D650,R201,D250,R528,D910,R670,U309,L658,U190,R704,U21,R288,D7,R930,U62,R782,U621,R328,D725,R305,U700,R494,D137,R969,U142,L867,U577,R300,U162,L13,D698,R333,U865,R941,U796,L60,U902,L784,U832,R78,D578,R196,D390,R728,D922,R858,D994,L457,U547,R238,D345,R329,D498,R873,D212,R501,U474,L657,U910,L335,U133,R213,U417,R698,U829,L2,U704,L273,D83,R231,D247,R675,D23,L692,D472,L325,D659,L408,U746,L715,U395,L596,U296,R52,D849,L713,U815,R684,D551,L319,U768,R176,D182,R557,U731,R314,D543,L9,D256,R38,D809,L567,D332,R375,D572,R81,D479,L71,U968,L831,D247,R989,U390,R463,D576,R740,D539,R488,U367,L596,U375,L763,D824,R70,U448,R979,D977,L744,D379,R488,D671,L516,D334,L542,U517,L488,D390,L713,D932,L28,U924,L448,D229,L488,D501,R19,D910,L979,D411,R711,D824,L973,U291,R794,D485,R208,U370,R655,U450,L40,D804,L374,D671,R962,D829,L209,U111,L84,D876,L832,D747,L733,D560,L702,D972,R188,U817,L111,U26,L492,U485,L71,D59,L269,D870,L152,U539,R65,D918,L932,D260,L485,U77,L699,U254,R924,U643,L264,U96,R395,D917,R360,U354,R101,D682,R854,U450,L376,D378,R872,D311,L881,U630,R77,D766,R672 3 | -------------------------------------------------------------------------------- /2019/04/4.pl: -------------------------------------------------------------------------------- 1 | repeat([H1, H2|_]) :- 2 | H1 = H2. 3 | repeat([_|T]) :- repeat(T). 4 | 5 | incr([]). 6 | incr([_]). 7 | incr([H1, H2|T]) :- 8 | H1 =< H2, incr([H2|T]). 9 | 10 | good(Z, 1) :- 11 | repeat(Z), incr(Z). 12 | good(Z, 2) :- 13 | clearRepeat(Z, 0), incr(Z). 14 | 15 | clearRepeat([H1, H2, H3|_], 0) :- 16 | H1 = H2, H2 \= H3. 17 | clearRepeat([H1, H2, H3, H4|_], _) :- 18 | H1 \= H2, H2 = H3, H3 \= H4. 19 | clearRepeat([H1, H2, H3], _) :- 20 | H1 \= H2, H2 = H3. 21 | clearRepeat([_|T], I) :- 22 | clearRepeat(T, I + 1). 23 | 24 | candidate(X, Question) :- 25 | between(356261, 846303, X), 26 | number_string(X, Y), string_codes(Y, Z), 27 | good(Z, Question). 28 | 29 | main :- 30 | aggregate(count, X, candidate(X, 1), N1), 31 | aggregate(count, X, candidate(X, 2), N2), 32 | writeln(N1), 33 | writeln(N2). 34 | -------------------------------------------------------------------------------- /2019/05/5.py: -------------------------------------------------------------------------------- 1 | OP_ADD = 1 2 | OP_MUL = 2 3 | OP_IN = 3 4 | OP_OUT = 4 5 | OP_HALT = 99 6 | OP_JMPT = 5 7 | OP_JMPF = 6 8 | OP_LT = 7 9 | OP_EQ = 8 10 | 11 | num_params = { 12 | OP_ADD: 3, 13 | OP_MUL: 3, 14 | OP_IN: 1, 15 | OP_OUT: 1, 16 | OP_HALT: 0, 17 | OP_JMPT: 2, 18 | OP_JMPF: 2, 19 | OP_LT: 3, 20 | OP_EQ: 3 21 | } 22 | 23 | with open("5.txt") as f: 24 | program = f.read() 25 | # program = "3,0,4,0,99" 26 | # program = "3,9,8,9,10,9,4,9,99,-1,8" 27 | # program = "3,9,7,9,10,9,4,9,99,-1,8" 28 | # program = "3,3,1108,-1,8,3,4,3,99" 29 | # program = "3,3,1107,-1,8,3,4,3,99" 30 | # program = "3,12,6,12,15,1,13,14,13,4,13,99,-1,0,1,9" 31 | # program = "3,3,1105,-1,9,1101,0,0,12,4,12,99,1" 32 | # program = "3,21,1008,21,8,20,1005,20,22,107,8,21,20,1006,20,31,"\ 33 | # "1106,0,36,98,0,0,1002,21,125,20,4,20,1105,1,46,104,"\ 34 | # "999,1105,1,46,1101,1000,1,20,4,20,1105,1,46,98,99" 35 | program = list(map(int, program.split(','))) 36 | 37 | def read(memory, ip, i, opcode): 38 | loc = memory[ip + i] 39 | if (opcode // 10**(i + 1)) % 10 == 0: 40 | return memory[loc] 41 | return loc 42 | 43 | def write(memory, ip, i, val): 44 | op_loc = memory[ip + i] 45 | memory[op_loc] = val 46 | 47 | def run(program): 48 | memory = {} 49 | for i, instruction in enumerate(program): 50 | memory[i] = instruction 51 | 52 | ip = 0 53 | while ip < len(program): 54 | opcode = memory[ip] 55 | operation = opcode % 100 56 | # print("Operation: " + str(operation)) 57 | if operation == OP_HALT: 58 | break 59 | elif operation == OP_MUL or operation == OP_ADD: 60 | lval = read(memory, ip, 1, opcode) 61 | rval = read(memory, ip, 2, opcode) 62 | if operation == OP_ADD: 63 | res = lval + rval 64 | elif operation == OP_MUL: 65 | res = lval * rval 66 | write(memory, ip, 3, res) 67 | elif operation == OP_IN: 68 | s = int(input('Input: ')) 69 | write(memory, ip, 1, int(s)) 70 | elif operation == OP_OUT: 71 | s = read(memory, ip, 1, opcode) 72 | print('Output: ' + str(s)) 73 | elif operation == OP_JMPT: 74 | val = read(memory, ip, 1, opcode) 75 | if val != 0: 76 | ip = read(memory, ip, 2, opcode) 77 | continue 78 | elif operation == OP_JMPF: 79 | val = read(memory, ip, 1, opcode) 80 | if val == 0: 81 | ip = read(memory, ip, 2, opcode) 82 | continue 83 | elif operation == OP_LT: 84 | lval = read(memory, ip, 1, opcode) 85 | rval = read(memory, ip, 2, opcode) 86 | if lval < rval: 87 | out = 1 88 | else: 89 | out = 0 90 | write(memory, ip, 3, out) 91 | elif operation == OP_EQ: 92 | lval = read(memory, ip, 1, opcode) 93 | rval = read(memory, ip, 2, opcode) 94 | if lval == rval: 95 | out = 1 96 | else: 97 | out = 0 98 | write(memory, ip, 3, out) 99 | else: 100 | print('Invalid opcode ' + str(opcode)) 101 | ip += num_params[operation] + 1 102 | else: 103 | print('Reached program end without halting') 104 | 105 | return memory[0] 106 | 107 | run(program) 108 | -------------------------------------------------------------------------------- /2019/06/6.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | 3 | with open('6.txt') as f: 4 | contents = f.read() 5 | 6 | adj = defaultdict(lambda: []) 7 | 8 | for line in contents.split("\n"): 9 | if line != "": 10 | u, v = line.split(")") 11 | adj[u].append(v) 12 | 13 | def countDescendants(u): 14 | c = 0 15 | q = [u] 16 | while len(q) > 0: 17 | v = q.pop() 18 | c += 1 19 | if v in adj: 20 | for w in adj[v]: 21 | q.append(w) 22 | 23 | return c - 1 24 | 25 | c = 0 26 | for u in adj: 27 | c += countDescendants(u) 28 | 29 | print(c) 30 | -------------------------------------------------------------------------------- /2019/06/6b.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | 3 | with open('6.txt') as f: 4 | contents = f.read() 5 | 6 | adj = defaultdict(lambda: set()) 7 | 8 | for line in contents.split("\n"): 9 | if line != "": 10 | u, v = line.split(")") 11 | adj[u].add(v) 12 | adj[v].add(u) 13 | 14 | def bfs(s, t): 15 | visited = set() 16 | q = [(s, 0)] 17 | while len(q) > 0: 18 | v, c = q.pop() 19 | visited.add(v) 20 | if v == t: 21 | return c 22 | for w in adj[v] - visited: 23 | q.append((w, c + 1)) 24 | 25 | print(bfs("YOU", "SAN") - 2) 26 | -------------------------------------------------------------------------------- /2019/07/7.py: -------------------------------------------------------------------------------- 1 | from computer import Computer 2 | 3 | with open("7.txt") as f: 4 | code = f.read() 5 | 6 | code = list(map(int, code.split(','))) 7 | 8 | from itertools import permutations 9 | from unittest.mock import Mock 10 | 11 | def solve(): 12 | NUM_AMPS = 5 13 | bestsignal = 0 14 | for permutation in permutations(range(NUM_AMPS)): 15 | signal = 0 16 | for phase in permutation: 17 | computer = Computer() 18 | computer.load(code) 19 | 20 | def in_func(): 21 | yield phase 22 | yield signal 23 | 24 | computer.input_func = in_func 25 | signal = next(computer.execute()) 26 | bestsignal = max(signal, bestsignal) 27 | return bestsignal 28 | 29 | print(solve()) 30 | -------------------------------------------------------------------------------- /2019/07/7b.py: -------------------------------------------------------------------------------- 1 | from computer import Computer, System, Pipe 2 | from itertools import permutations 3 | 4 | with open("7.txt") as f: 5 | code = f.read() 6 | 7 | code = list(map(int, code.split(','))) 8 | 9 | def solve(): 10 | NUM_AMPS = 5 11 | bestsignal = 0 12 | for permutation in permutations(range(NUM_AMPS, 2 * NUM_AMPS)): 13 | system = System() 14 | for i, phase in enumerate(permutation): 15 | computer = Computer() 16 | computer.load(code) 17 | system.add(computer) 18 | if i == 0: 19 | first_computer = computer 20 | else: 21 | pipe = system.connect(prev_computer, computer) 22 | pipe.write_silent(phase) 23 | prev_computer = computer 24 | feedback_pipe = system.connect(prev_computer, first_computer) 25 | feedback_pipe.write_silent(permutation[0]) 26 | feedback_pipe.write(0) 27 | 28 | signal = feedback_pipe.read() 29 | bestsignal = max(signal, bestsignal) 30 | 31 | return bestsignal 32 | 33 | print(solve()) 34 | -------------------------------------------------------------------------------- /2019/07/computer.py: -------------------------------------------------------------------------------- 1 | ../9/computer.py -------------------------------------------------------------------------------- /2019/08/8.py: -------------------------------------------------------------------------------- 1 | W = 25 2 | H = 6 3 | 4 | with open('8.txt') as f: 5 | data = f.read() 6 | 7 | data = data[:-1] 8 | data = list(map(int, data)) 9 | 10 | size = W * H 11 | layers = len(data) // size 12 | 13 | best_count = size + 1 14 | best_layer = None 15 | 16 | def get_data(layer): 17 | return data[layer * size:(layer + 1) * size] 18 | 19 | for layer in range(layers): 20 | layer_data = get_data(layer) 21 | count = 0 22 | for pixel in layer_data: 23 | if pixel == 0: 24 | count += 1 25 | if count < best_count: 26 | best_count = count 27 | best_layer = layer 28 | 29 | layer_data = get_data(best_layer) 30 | count1 = 0 31 | count2 = 0 32 | for pixel in layer_data: 33 | if pixel == 1: 34 | count1 += 1 35 | elif pixel == 2: 36 | count2 += 1 37 | print(count1 * count2) 38 | 39 | image = [0] * size 40 | for layer in range(0, layers)[::-1]: 41 | layer_data = get_data(layer) 42 | for i, pixel in enumerate(layer_data): 43 | if pixel == 2: 44 | continue 45 | image[i] = pixel 46 | 47 | for row in range(H): 48 | print(image[row * W:(row + 1) * W]) 49 | -------------------------------------------------------------------------------- /2019/09/9.py: -------------------------------------------------------------------------------- 1 | from computer import Computer 2 | 3 | computer = Computer() 4 | computer.load_file("9.txt") 5 | computer.run() 6 | -------------------------------------------------------------------------------- /2019/09/computer.py: -------------------------------------------------------------------------------- 1 | from inspect import signature 2 | from collections import defaultdict, deque 3 | import copy 4 | 5 | OP_ADD = 1 6 | OP_MUL = 2 7 | OP_IN = 3 8 | OP_OUT = 4 9 | OP_HALT = 99 10 | OP_JMPT = 5 11 | OP_JMPF = 6 12 | OP_LT = 7 13 | OP_EQ = 8 14 | OP_REL = 9 15 | 16 | MEMORY_POSITION = 0 17 | MEMORY_RELATIVE = 2 18 | MEMORY_IMMEDIATE = 1 19 | 20 | class IOAction(Exception): 21 | pass 22 | 23 | class IOInputAction(IOAction): 24 | def __init__(self): 25 | self.value = None 26 | 27 | class IOOutputAction(IOAction): 28 | def __init__(self, value): 29 | self.value = value 30 | 31 | class Pipe: 32 | def __init__(self): 33 | self.data = deque() 34 | self.subscribers = [] 35 | 36 | def __repr__(self): 37 | return self.name 38 | 39 | def write_silent(self, x): 40 | self.data.appendleft(x) 41 | 42 | def write(self, x): 43 | self.write_silent(x) 44 | for subscriber in self.subscribers: 45 | subscriber() 46 | 47 | def read(self): 48 | return self.data.pop() 49 | 50 | def subscribe(self, cb): 51 | self.subscribers.append(cb) 52 | 53 | def empty(self): 54 | return len(self.data) == 0 55 | 56 | class System: 57 | def __init__(self): 58 | self.computers = {} 59 | self.pipes = {} 60 | 61 | def add(self, computer): 62 | self.computers[computer] = { 63 | 'computer': computer, 64 | 'state': computer.execute(), 65 | } 66 | 67 | def connect(self, source, target): 68 | pipe = Pipe() 69 | action = None 70 | self.pipes[source] = pipe 71 | 72 | def data_available(): 73 | nonlocal action 74 | 75 | while self.computers[target]['state'] is not None: 76 | if isinstance(action, IOInputAction): 77 | if pipe.empty(): 78 | return 79 | action.value = pipe.read() 80 | try: 81 | action = next(self.computers[target]['state']) 82 | except StopIteration: 83 | self.computers[target]['state'] = None 84 | break 85 | if isinstance(action, IOOutputAction): 86 | self.pipes[target].write(action.value) 87 | elif isinstance(action, IOInputAction): 88 | pending_action = action 89 | if pipe.empty(): 90 | break 91 | 92 | pipe.subscribe(data_available) 93 | return pipe 94 | 95 | class Computer: 96 | _handlers = {} 97 | _num_params = {} 98 | 99 | class ExecutionCompleted(Exception): 100 | pass 101 | 102 | def __repr__(self): 103 | return self.name 104 | 105 | def __init__(self): 106 | self._init_handlers() 107 | self.program = [] 108 | self._current_opcode = None 109 | self._program = None 110 | self._base = 0 111 | 112 | def __repr__(self): 113 | return self.name 114 | 115 | def _handle(self, operation): 116 | def decorate(func): 117 | self._handlers[operation] = func 118 | sig = signature(func) 119 | self._num_params[operation] = len(sig.parameters.items()) - 1 120 | return func 121 | return decorate 122 | 123 | def _jump(self, ip): 124 | self._ip = ip 125 | self._ip_manual = True 126 | 127 | def _dispatch(self, operation, handler, opcode): 128 | args = [] 129 | for i in range(1, self._num_params[operation] + 1): 130 | args.append(self._read(i, opcode)) 131 | ret = handler(self, *args) 132 | if ret is not None: 133 | yield from ret 134 | if self._ip_manual: 135 | self._ip_manual = False 136 | else: 137 | self._ip += self._num_params[operation] + 1 138 | 139 | def _init_handlers(self): 140 | @self._handle(OP_ADD) 141 | def _add(self, x, y, _): 142 | self._write(3, x + y) 143 | 144 | @self._handle(OP_MUL) 145 | def _mul(self, x, y, _): 146 | self._write(3, x * y) 147 | 148 | @self._handle(OP_IN) 149 | def _in(self, _): 150 | action = IOInputAction() 151 | yield action 152 | if action.value is None: 153 | raise ValueError('No input provided') 154 | self._write(1, action.value) 155 | 156 | @self._handle(OP_OUT) 157 | def _out(self, x): 158 | yield IOOutputAction(x) 159 | 160 | @self._handle(OP_LT) 161 | def _lt(self, x, y, _): 162 | self._write(3, x < y) 163 | 164 | @self._handle(OP_EQ) 165 | def _eq(self, x, y, _): 166 | self._write(3, x == y) 167 | 168 | @self._handle(OP_HALT) 169 | def _halt(self): 170 | raise self.ExecutionCompleted 171 | 172 | @self._handle(OP_JMPT) 173 | def _jmpt(self, cond, target): 174 | if cond: 175 | self._jump(target) 176 | 177 | @self._handle(OP_JMPF) 178 | def _jmpf(self, cond, target): 179 | if not cond: 180 | self._jump(target) 181 | 182 | @self._handle(OP_REL) 183 | def _rel(self, base): 184 | self._base += base 185 | 186 | def _getmode(self, i, opcode): 187 | return (opcode // 10**(i + 1)) % 10 188 | 189 | def _read(self, i, opcode): 190 | loc = self.memory[self._ip + i] 191 | mode = self._getmode(i, opcode) 192 | return { 193 | MEMORY_POSITION: self.memory[loc], 194 | MEMORY_RELATIVE: self.memory[self._base + loc], 195 | MEMORY_IMMEDIATE: loc 196 | }[mode] 197 | 198 | def _write(self, i, val, opcode=None): 199 | if opcode is None: 200 | opcode = self._current_opcode 201 | op_loc = self.memory[self._ip + i] 202 | mode = self._getmode(i, opcode) 203 | if mode == MEMORY_POSITION: 204 | self.memory[op_loc] = int(val) 205 | elif mode == MEMORY_RELATIVE: 206 | self.memory[self._base + op_loc] = int(val) 207 | else: 208 | raise 'Invalid memory mode for writing ' + str(mode) 209 | 210 | def init(self): 211 | self._ip = 0 212 | self._ip_manual = False 213 | self.memory = defaultdict(lambda: 0) 214 | for i, instruction in enumerate(self._program): 215 | self.memory[i] = instruction 216 | 217 | def load(self, program): 218 | self._program = program 219 | self.init() 220 | 221 | def load_file(self, filename): 222 | with open(filename) as f: 223 | code = f.read() 224 | 225 | code = list(map(int, code.split(','))) 226 | self.load(code) 227 | 228 | def read_from(self): 229 | action = next(self._execution) 230 | assert(isinstance(action, IOOutputAction)) 231 | return action.value 232 | 233 | def write_to(self, value): 234 | action = next(self._execution) 235 | assert(isinstance(action, IOInputAction)) 236 | action.value = value 237 | 238 | def execute(self): 239 | self._execution = self._execute() 240 | return self._execution 241 | 242 | def _execute(self): 243 | while self._ip < len(self.memory): 244 | opcode = self.memory[self._ip] 245 | self._current_opcode = opcode 246 | operation = opcode % 100 247 | if operation not in self._handlers: 248 | raise ValueError('Invalid opcode ' + str(opcode)) 249 | try: 250 | handler = self._handlers[operation] 251 | action = self._dispatch(operation, handler, opcode) 252 | if action is not None: 253 | yield from action 254 | except self.ExecutionCompleted: 255 | break 256 | else: 257 | raise EOFError('Reached program end without halting') 258 | 259 | def save(self): 260 | return [self._ip, copy.deepcopy(self.memory)] 261 | 262 | def restore(self, state): 263 | self._ip = state[0] 264 | self.memory = defaultdict(lambda: 0, state[1]) 265 | 266 | def run(self): 267 | for action in self.execute(): 268 | if isinstance(action, IOInputAction): 269 | action.value = input('Input: ') 270 | elif isinstance(action, IOOutputAction): 271 | print(action.value) 272 | -------------------------------------------------------------------------------- /2019/10/10.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from heapq import heappush, heappop 3 | from collections import defaultdict 4 | 5 | ERROR = 10 6 | EPSILON = 10**(-ERROR) 7 | 8 | with open('10.txt') as f: 9 | rows = f.read().split('\n')[:-1] 10 | 11 | asteroids = set() 12 | for y, row in enumerate(rows): 13 | for x, marker in enumerate(row): 14 | if marker == '#': 15 | asteroids.add(x + y * 1j) 16 | 17 | best_count = 0 18 | for candidate_station in asteroids: 19 | field_of_view = defaultdict(lambda: []) 20 | for optical_target in asteroids.difference([candidate_station]): 21 | num = optical_target - candidate_station 22 | angle = round(np.angle(num * 1j), ERROR) % (2 * np.pi) 23 | heappush(field_of_view[angle], (np.absolute(num), optical_target)) 24 | if len(field_of_view) > best_count: 25 | best_count = len(field_of_view) 26 | best_field_of_view = field_of_view 27 | station = candidate_station 28 | 29 | destroyed = [] 30 | survivors = asteroids.difference([station]) 31 | destruction_path = sorted(best_field_of_view.items()) 32 | 33 | while len(survivors): 34 | for angle, beam in destruction_path: 35 | if len(beam): 36 | _, victim = heappop(beam) 37 | destroyed.append(victim) 38 | survivors.remove(victim) 39 | 40 | print(100 * destroyed[199].real + destroyed[199].imag) 41 | -------------------------------------------------------------------------------- /2019/11/11.py: -------------------------------------------------------------------------------- 1 | from computer import Computer 2 | from collections import defaultdict 3 | 4 | computer = Computer() 5 | computer.load_file("11.txt") 6 | color_at = defaultdict(lambda: 0) 7 | 8 | def paint(computer, color_at): 9 | modified = set() 10 | direction = 1j 11 | location = 0 12 | execution = computer.execute() 13 | 14 | while True: 15 | try: 16 | computer.write_to(color_at[location]) 17 | color_at[location] = computer.read_from() 18 | modified.add((int(location.real), int(location.imag))) 19 | turn = computer.read_from() 20 | direction *= 1j 21 | direction *= turn * 2 - 1 22 | location += direction 23 | except StopIteration: 24 | return modified 25 | 26 | print(len(paint(computer, color_at))) 27 | color_at = defaultdict(lambda: 0) 28 | color_at[0] = 1 29 | modified = paint(computer, color_at) 30 | xs, ys = zip(*list(modified)) 31 | for y in reversed(range(min(ys), max(ys) + 1)): 32 | row = [] 33 | for x in reversed(range(min(xs), max(xs) + 1)): 34 | if color_at[x + y * 1j] == 0: 35 | row.append('.') 36 | else: 37 | row.append('#') 38 | print(''.join(row)) 39 | -------------------------------------------------------------------------------- /2019/11/computer.py: -------------------------------------------------------------------------------- 1 | ../9/computer.py -------------------------------------------------------------------------------- /2019/12/12.py: -------------------------------------------------------------------------------- 1 | import re 2 | import numpy as np 3 | 4 | with open("12.txt") as f: 5 | contents = f.readlines() 6 | 7 | class Moon: 8 | def __init__(self, position): 9 | self.position = position 10 | self.velocity = np.array([0, 0, 0]) 11 | 12 | def energy(self): 13 | pot = np.sum(np.abs(self.position)) 14 | kin = np.sum(np.abs(self.velocity)) 15 | 16 | return pot*kin 17 | 18 | def __repr__(self): 19 | return 'pos=, vel=' 26 | 27 | moons = [] 28 | for line in contents: 29 | match = re.match(r".*=([-0-9]+).*=([-0-9]+).*=([-0-9]+)", line) 30 | position = np.array(list(map(int, match.groups()))) 31 | moons.append(Moon(position)) 32 | 33 | TARGET_STEP = 1000 34 | 35 | step = 0 36 | snapshots = [set(), set(), set()] 37 | repeat = [None, None, None] 38 | while True: 39 | for dimension in range(3): 40 | if repeat[dimension] is not None: 41 | continue 42 | snapshot = [] 43 | for moon in moons: 44 | snapshot.append((moon.position[dimension], moon.velocity[dimension])) 45 | snapshot = tuple(snapshot) 46 | if snapshot in snapshots[dimension]: 47 | repeat[dimension] = step 48 | snapshots[dimension].add(snapshot) 49 | if all(repeat): 50 | break 51 | if step == TARGET_STEP: 52 | s = 0 53 | for moon in moons: 54 | s += moon.energy() 55 | print('Total energy after step ' + str(step) + ':') 56 | print(s) 57 | step += 1 58 | for moon in moons: 59 | for other_moon in moons: 60 | moon.velocity += np.sign(other_moon.position - moon.position) 61 | for moon in moons: 62 | moon.position += moon.velocity 63 | print(np.lcm.reduce(repeat)) 64 | -------------------------------------------------------------------------------- /2019/13/13.py: -------------------------------------------------------------------------------- 1 | from computer import Computer 2 | 3 | computer = Computer() 4 | computer.load_file("13.txt") 5 | computer.execute() 6 | 7 | count = 0 8 | while True: 9 | try: 10 | x = computer.read_from() 11 | y = computer.read_from() 12 | tile = computer.read_from() 13 | if tile == 2: 14 | count += 1 15 | except StopIteration: 16 | break 17 | 18 | print(count) 19 | -------------------------------------------------------------------------------- /2019/13/13b.py: -------------------------------------------------------------------------------- 1 | from computer import Computer, IOInputAction, IOOutputAction 2 | from curses import wrapper, KEY_LEFT, KEY_RIGHT 3 | from time import sleep 4 | from collections import defaultdict 5 | from copy import deepcopy 6 | 7 | tile_types = { 8 | 0: ' ', 9 | 1: '#', 10 | 2: '@', 11 | 3: '-', 12 | 4: '*' 13 | } 14 | 15 | def main(): 16 | score = 0 17 | count = 0 18 | 19 | def game(stdscr): 20 | nonlocal score 21 | nonlocal count 22 | 23 | computer = Computer() 24 | computer.load_file("13.txt") 25 | computer.memory[0] = 2 26 | input_order = -1 27 | x = 0 28 | y = 0 29 | past = [] 30 | playing = True 31 | screen = {} 32 | 33 | while playing: 34 | for action in computer.execute(): 35 | if isinstance(action, IOOutputAction): 36 | input_order += 1 37 | input_order %= 3 38 | if input_order == 0: 39 | x = action.value 40 | elif input_order == 1: 41 | y = action.value 42 | elif input_order == 2: 43 | tile = action.value 44 | if x == -1 and y == 0: 45 | score = tile 46 | else: 47 | stdscr.addch(y, x, tile_types[tile]) 48 | stdscr.refresh() 49 | screen[(y, x)] = tile_types[tile] 50 | elif isinstance(action, IOInputAction): 51 | past.append((computer.save(), deepcopy(screen))) 52 | count += 1 53 | key_press = stdscr.getch() 54 | if key_press == KEY_LEFT: 55 | action.value = -1 56 | elif key_press == KEY_RIGHT: 57 | action.value = 1 58 | elif key_press == ord('z'): 59 | past.pop() 60 | break 61 | else: 62 | action.value = 0 63 | else: 64 | playing = False 65 | if playing: 66 | past.pop() 67 | state, screen = past.pop() 68 | computer.restore(state) 69 | stdscr.clear() 70 | for (y, x), tile in screen.items(): 71 | stdscr.addch(y, x, tile) 72 | 73 | wrapper(game) 74 | print('Count: ' + str(count)) 75 | print('Score: ' + str(score)) 76 | 77 | main() 78 | -------------------------------------------------------------------------------- /2019/13/computer.py: -------------------------------------------------------------------------------- 1 | ../9/computer.py -------------------------------------------------------------------------------- /2019/14/14.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | with open('14.txt') as f: 4 | lines = f.read().split('\n') 5 | 6 | def parse_material(str): 7 | amount, ingredient = str.split(' ') 8 | return int(amount), ingredient 9 | 10 | nodes = {'ORE': {'make_amount': 1, 11 | 'ingredients': []}} 12 | 13 | for line in lines[:-1]: 14 | src, dst = line.split(' => ') 15 | amount, dst = parse_material(dst) 16 | ingredients = list(map(parse_material, src.split(', '))) 17 | nodes[dst] = {'make_amount': amount, 18 | 'ingredients': ingredients} 19 | 20 | def ore_needed(fuel): 21 | for node in nodes: 22 | nodes[node]['used'] = nodes[node]['capacity'] = 0 23 | 24 | nodes['FUEL']['used'] = fuel 25 | q = ['FUEL'] 26 | while len(q): 27 | node = nodes[q.pop()] 28 | if node['used'] > node['capacity']: 29 | need_to_make = node['used'] - node['capacity'] 30 | reactions = math.ceil(need_to_make / node['make_amount']) 31 | node['capacity'] += node['make_amount'] * reactions 32 | for ingredient_amount, ingredient in node['ingredients']: 33 | nodes[ingredient]['used'] += ingredient_amount * reactions 34 | q.append(ingredient) 35 | 36 | return nodes['ORE']['capacity'] 37 | 38 | print(ore_needed(1)) 39 | 40 | ORE_LIMIT = 1000000000000 41 | lo = 0 # inclusive 42 | hi = ORE_LIMIT + 1 # exclusive 43 | while hi > lo + 1: 44 | mid = (hi + lo) // 2 45 | if ore_needed(mid) > ORE_LIMIT: 46 | hi = mid 47 | else: 48 | lo = mid 49 | 50 | print(lo) 51 | -------------------------------------------------------------------------------- /2019/15/15.py: -------------------------------------------------------------------------------- 1 | from computer import Computer 2 | from collections import deque 3 | 4 | DIR_NORTH = 1 5 | DIR_SOUTH = 2 6 | DIR_WEST = 3 7 | DIR_EAST = 4 8 | 9 | DIRECTIONS = [-1j, 1j, -1, 1] 10 | 11 | instruction_from_direction = { 12 | -1j: DIR_NORTH, 13 | 1j: DIR_SOUTH, 14 | -1: DIR_WEST, 15 | 1: DIR_EAST 16 | } 17 | 18 | LOC_OPEN = '.' 19 | LOC_WALL = '#' 20 | 21 | RESP_FAIL = 0 22 | RESP_MOVED = 1 23 | RESP_FOUND = 2 24 | 25 | def shortest_path(world, src, dst): 26 | q = [src] 27 | visited = set() 28 | parent = {} 29 | while len(q): 30 | node = q.pop() 31 | visited.add(node) 32 | if node == dst: 33 | path = [node] 34 | while node != src: 35 | node = parent[node] 36 | path.append(node) 37 | path.reverse() 38 | return path 39 | for delta in DIRECTIONS: 40 | neighbour = node + delta 41 | if neighbour in world and world[neighbour] == LOC_OPEN \ 42 | or neighbour not in world and neighbour == dst: 43 | if not neighbour in visited: 44 | q.append(neighbour) 45 | parent[neighbour] = node 46 | 47 | def print_world(world): 48 | locations = list(world.keys()) 49 | real = list(map(lambda x: int(x.real), locations)) 50 | imag = list(map(lambda x: int(x.imag), locations)) 51 | for y in range(min(imag), max(imag) + 1): 52 | row = [] 53 | for x in range(min(real), max(real) + 1): 54 | node = x + y * 1j 55 | if node == 0: 56 | row.append('X') 57 | elif node in world: 58 | row.append(world[node]) 59 | else: 60 | row.append('?') 61 | print(''.join(row)) 62 | 63 | def solve(): 64 | def move_step(direction): 65 | computer.write_to(instruction_from_direction[direction]) 66 | return computer.read_from() 67 | 68 | def go_to(target): 69 | nonlocal current_location 70 | nonlocal oxygen_location 71 | 72 | path = shortest_path(world, current_location, target) 73 | if path is None: 74 | return False, [] 75 | path = path[1:] 76 | for node in path: 77 | delta = node - current_location 78 | response = move_step(delta) 79 | if response != RESP_FAIL: 80 | current_location = node 81 | if node == target: 82 | if response == RESP_FOUND: 83 | solution = shortest_path(world, 0, target)[1:] 84 | oxygen_location = target 85 | print('Found at: ' + str(len(solution))) 86 | if response == RESP_FAIL: 87 | world[node] = LOC_WALL 88 | return False, [] 89 | world[node] = LOC_OPEN 90 | ret = [] 91 | for delta in DIRECTIONS: 92 | if node + delta not in world: 93 | ret.append(node + delta) 94 | return response == RESP_FOUND, ret 95 | assert(response != RESP_FAIL) 96 | 97 | computer = Computer() 98 | computer.load_file('15.txt') 99 | computer.execute() 100 | 101 | current_location = 0 102 | oxygen_location = None 103 | 104 | world = {} 105 | world[0] = LOC_OPEN 106 | unexplored = deque() 107 | for delta in DIRECTIONS: 108 | unexplored.append(current_location + delta) 109 | 110 | while len(unexplored): 111 | node = unexplored.popleft() 112 | found, frontier = go_to(node) 113 | unexplored.extend(frontier) 114 | 115 | diameter = 0 116 | for location in world.keys(): 117 | if world[location] == LOC_OPEN: 118 | distance = len(shortest_path(world, oxygen_location, location)[1:]) 119 | diameter = max(diameter, distance) 120 | print('Diameter: ' + str(diameter)) 121 | 122 | solve() 123 | -------------------------------------------------------------------------------- /2020/1/1.py: -------------------------------------------------------------------------------- 1 | with open('in.txt') as f: 2 | nums = set(map(int, f.readlines())) 3 | 4 | for x in nums: 5 | if 2020 - x in nums: 6 | print(x * (2020 - x)) 7 | break 8 | -------------------------------------------------------------------------------- /2020/1/1b.py: -------------------------------------------------------------------------------- 1 | from itertools import product 2 | 3 | with open('in2.txt') as f: 4 | nums = set(map(int, f.readlines())) 5 | 6 | for x, y in product(nums, nums): 7 | if 2020 - (x + y) in nums: 8 | print(x * y * (2020 - (x + y))) 9 | break 10 | -------------------------------------------------------------------------------- /2020/10/10.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict, Counter 2 | 3 | with open('in.txt') as f: 4 | lines = list(map(int, f.read().splitlines())) 5 | 6 | lines.sort() 7 | diffs = Counter({3: 1}) 8 | prev = 0 9 | for line in lines: 10 | diffs[line - prev] += 1 11 | prev = line 12 | 13 | print(diffs[1] * diffs[3]) 14 | 15 | cnt = defaultdict(int) 16 | cnt[0] = 1 17 | for line in lines: 18 | cnt[line] = cnt[line - 1] + cnt[line - 2] + cnt[line - 3] 19 | 20 | print(cnt[lines[-1]]) 21 | -------------------------------------------------------------------------------- /2020/11/11.py: -------------------------------------------------------------------------------- 1 | from copy import deepcopy 2 | from collections import Counter 3 | 4 | with open('in.txt') as f: 5 | prev_board = list(map(list, f.read().splitlines())) 6 | 7 | while True: 8 | board = deepcopy(prev_board) 9 | for y, line in enumerate(prev_board): 10 | for x, char in enumerate(line): 11 | cnt = 0 12 | for dx in (-1, 0, 1): 13 | for dy in (-1, 0, 1): 14 | yy = y + dy 15 | xx = x + dx 16 | if 0 <= yy < len(board) and 0 <= xx < len(board[0]): 17 | if (dx != 0 or dy != 0) and prev_board[yy][xx] == '#': 18 | cnt += 1 19 | if char == 'L' and cnt == 0: 20 | board[y][x] = '#' 21 | if char == '#' and cnt >= 4: 22 | board[y][x] = 'L' 23 | if board == prev_board: 24 | break 25 | prev_board = board 26 | 27 | print(sum(line.count('#') for line in board)) 28 | -------------------------------------------------------------------------------- /2020/11/11b.py: -------------------------------------------------------------------------------- 1 | from copy import deepcopy 2 | from collections import Counter 3 | 4 | with open('in.txt') as f: 5 | prev_board = list(map(list, f.read().splitlines())) 6 | 7 | while True: 8 | board = deepcopy(prev_board) 9 | for y, line in enumerate(prev_board): 10 | for x, char in enumerate(line): 11 | cnt = 0 12 | for dx in (-1, 0, 1): 13 | for dy in (-1, 0, 1): 14 | if dx == 0 and dy == 0: 15 | continue 16 | yy = y + dy 17 | xx = x + dx 18 | while 0 <= yy < len(board) and 0 <= xx < len(board[0]): 19 | if prev_board[yy][xx] == '#': 20 | cnt += 1 21 | break 22 | if prev_board[yy][xx] == 'L': 23 | break 24 | yy += dy 25 | xx += dx 26 | if char == 'L' and cnt == 0: 27 | board[y][x] = '#' 28 | if char == '#' and cnt >= 5: 29 | board[y][x] = 'L' 30 | if board == prev_board: 31 | break 32 | prev_board = board 33 | 34 | print(sum(line.count('#') for line in board)) 35 | -------------------------------------------------------------------------------- /2020/12/12.py: -------------------------------------------------------------------------------- 1 | from pygame.math import Vector2 2 | 3 | with open('in.txt') as f: 4 | lines = f.read().splitlines() 5 | 6 | location = Vector2(0, 0) 7 | direction = Vector2(1, 0) 8 | for line in lines: 9 | instruction, amount = line[0], int(line[1:]) 10 | cardinals = { 11 | 'E': (1, 0), 12 | 'W': (-1, 0), 13 | 'N': (0, -1), 14 | 'S': (0, 1), 15 | 'F': direction 16 | } 17 | if instruction in cardinals: 18 | delta = Vector2(cardinals[line[0]]) 19 | location += amount * delta 20 | if instruction == 'L': 21 | direction.rotate_ip(-amount) 22 | elif instruction == 'R': 23 | direction.rotate_ip(amount) 24 | 25 | print(sum(map(abs, location))) 26 | -------------------------------------------------------------------------------- /2020/12/12b.py: -------------------------------------------------------------------------------- 1 | from pygame.math import Vector2 2 | 3 | with open('in.txt') as f: 4 | lines = f.read().splitlines() 5 | 6 | location = Vector2(0, 0) 7 | waypoint = Vector2(10, -1) 8 | for line in lines: 9 | instruction, amount = line[0], int(line[1:]) 10 | cardinals = { 11 | 'E': (1, 0), 12 | 'W': (-1, 0), 13 | 'N': (0, -1), 14 | 'S': (0, 1) 15 | } 16 | if instruction in cardinals: 17 | delta = Vector2(cardinals[instruction]) 18 | waypoint += amount * delta 19 | elif instruction == 'F': 20 | location += amount * waypoint 21 | elif instruction == 'L': 22 | waypoint.rotate_ip(-amount) 23 | elif instruction == 'R': 24 | waypoint.rotate_ip(amount) 25 | 26 | print(sum(map(abs, location))) 27 | -------------------------------------------------------------------------------- /2020/13/13.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | with open('in.txt') as f: 4 | timestamp, buses = f.read().splitlines() 5 | 6 | timestamp = int(timestamp) 7 | buses = map(int, filter(lambda x: x != 'x', buses.split(','))) 8 | best_time = math.inf 9 | best_bus = None 10 | for bus in buses: 11 | time = bus - (timestamp % bus) 12 | if time <= best_time: 13 | best_bus = bus 14 | best_time = time 15 | print(best_time * best_bus) 16 | -------------------------------------------------------------------------------- /2020/13/13b.py: -------------------------------------------------------------------------------- 1 | from sympy.ntheory.modular import crt 2 | 3 | with open('in.txt') as f: 4 | _, buses = f.read().splitlines() 5 | 6 | moduli = [] 7 | residues = [] 8 | for i, bus in enumerate(buses.split(',')): 9 | if bus != 'x': 10 | bus = int(bus) 11 | moduli.append(bus) 12 | residues.append(bus - i) 13 | 14 | print(crt(moduli, residues)[0]) 15 | -------------------------------------------------------------------------------- /2020/14/14.py: -------------------------------------------------------------------------------- 1 | from parse import parse 2 | from collections import defaultdict 3 | 4 | with open('in.txt') as f: 5 | lines = f.read().splitlines() 6 | 7 | def apply_mask(mask, value): 8 | value |= int(mask.replace('X', '0'), 2) 9 | value &= int(mask.replace('X', '1'), 2) 10 | return value 11 | 12 | mask = 0 13 | mem = defaultdict(int) 14 | for line in lines: 15 | var, value = line.split(' = ') 16 | if var == 'mask': 17 | mask = value 18 | else: 19 | value = int(value) 20 | address, = parse('mem[{:d}]', var) 21 | value = apply_mask(mask, value) 22 | mem[address] = value 23 | 24 | print(sum(mem.values())) 25 | -------------------------------------------------------------------------------- /2020/14/14b.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | from parse import parse 3 | 4 | with open('in.txt') as f: 5 | lines = f.read().splitlines() 6 | 7 | def apply_mask(mask, value): 8 | value |= int(mask.replace('X', '0'), 2) 9 | value = bin(value)[2:].rjust(36, '0') 10 | values = [''] 11 | for j, x in enumerate(mask): 12 | if x == 'X': 13 | new_values = [] 14 | for option in values: 15 | new_values.append(option + '0') 16 | new_values.append(option + '1') 17 | values = new_values 18 | else: 19 | for i, option in enumerate(values): 20 | values[i] += value[j] 21 | 22 | return values 23 | 24 | mask = 0 25 | mem = defaultdict(int) 26 | for line in lines: 27 | var, value = line.split(' = ') 28 | if var == 'mask': 29 | mask = value 30 | else: 31 | value = int(value) 32 | address, = parse('mem[{:d}]', var) 33 | for address in apply_mask(mask, var): 34 | mem[address] = value 35 | 36 | print(sum(mem.values())) 37 | -------------------------------------------------------------------------------- /2020/15/15.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | 3 | with open('in.txt') as f: 4 | nums = list(map(int, f.read().split(','))) 5 | 6 | num_turns = defaultdict(list) 7 | for turn, num in enumerate(nums): 8 | num_turns[num].append(turn + 1) 9 | num = nums[-1] 10 | 11 | INTERESTING = 2020, 30000000 12 | 13 | for turn in range(len(nums) + 1, INTERESTING[-1] + 1): 14 | prev_turns = num_turns[num] 15 | try: 16 | num = prev_turns[-1] - prev_turns[-2] 17 | except: 18 | num = 0 19 | num_turns[num].append(turn) 20 | if turn in INTERESTING: 21 | print(num) 22 | -------------------------------------------------------------------------------- /2020/16/16.py: -------------------------------------------------------------------------------- 1 | from parse import parse 2 | from functools import partial 3 | 4 | with open('in.txt') as f: 5 | rules, your, other = f.read().split('\n\n') 6 | 7 | def match(a_lo, a_hi, b_lo, b_hi, ticket): 8 | return a_lo <= ticket <= a_hi\ 9 | or b_lo <= ticket <= b_hi 10 | 11 | rules = rules.splitlines() 12 | evalrule = {} 13 | for rule in rules: 14 | fieldname, ranges = rule.split(': ') 15 | a_lo, a_hi, b_lo, b_hi = parse('{:d}-{:d} or {:d}-{:d}', ranges) 16 | evalrule[fieldname] = partial(match, a_lo, a_hi, b_lo, b_hi) 17 | 18 | other = other.splitlines()[1:] 19 | other = list(map(lambda x: list(map(int, x.split(','))), other)) 20 | 21 | cnt = 0 22 | for ticket in other: 23 | for value in ticket: 24 | if not any(rule(value) for rule in evalrule.values()): 25 | cnt += value 26 | print(cnt) 27 | -------------------------------------------------------------------------------- /2020/16/16b.py: -------------------------------------------------------------------------------- 1 | from parse import parse 2 | from functools import partial 3 | 4 | with open('in.txt') as f: 5 | rules, your, other = f.read().split('\n\n') 6 | 7 | def match(a_lo, a_hi, b_lo, b_hi, ticket): 8 | return a_lo <= ticket <= a_hi\ 9 | or b_lo <= ticket <= b_hi 10 | 11 | rules = rules.splitlines() 12 | evalrule = {} 13 | for rule in rules: 14 | fieldname, ranges = rule.split(': ') 15 | a_lo, a_hi, b_lo, b_hi = parse('{:d}-{:d} or {:d}-{:d}', ranges) 16 | evalrule[fieldname] = partial(match, a_lo, a_hi, b_lo, b_hi) 17 | 18 | your = your.splitlines()[1] 19 | your = list(map(int, your.split(','))) 20 | other = other.splitlines()[1:] 21 | other = list(map(lambda x: list(map(int, x.split(','))), other)) 22 | 23 | good_tickets = [] 24 | 25 | for ticket in other: 26 | for value in ticket: 27 | if not any(rule(value) for rule in evalrule.values()): 28 | break 29 | else: 30 | good_tickets.append(ticket) 31 | 32 | fieldsets = [set(evalrule.keys()) for _ in your] 33 | 34 | for ticket in good_tickets: 35 | for ruleset, value in zip(fieldsets, ticket): 36 | failed = set(name for name, rule in evalrule.items() if not rule(value)) 37 | ruleset -= failed 38 | 39 | clear = set() 40 | while True: 41 | for j, fieldset in enumerate(fieldsets): 42 | fieldname = list(fieldset)[0] 43 | if len(fieldset) == 1 and fieldname not in clear: 44 | for i, otherfieldset in enumerate(fieldsets): 45 | if i != j: 46 | otherfieldset.discard(fieldname) 47 | clear.add(fieldname) 48 | break 49 | else: 50 | break 51 | 52 | fields = map(lambda x: x.pop(), fieldsets) 53 | 54 | mul = 1 55 | for fieldname, value in zip(fields, your): 56 | if fieldname.startswith('departure'): 57 | mul *= value 58 | print(mul) 59 | -------------------------------------------------------------------------------- /2020/17/17.py: -------------------------------------------------------------------------------- 1 | from collections import Counter 2 | from itertools import product 3 | from operator import add 4 | 5 | def solve(lines, cycles, dimensions): 6 | board = set() 7 | for row, line in enumerate(lines): 8 | for col, elem in enumerate(line): 9 | if elem == '#': 10 | cell = dimensions * [0,] 11 | cell[0], cell[1] = col, row 12 | board.add(tuple(cell)) 13 | 14 | for _ in range(cycles): 15 | new_board = set() 16 | 17 | neighbour_counts = Counter() 18 | for cell in board: 19 | for delta in product(range(-1, 2), repeat=dimensions): 20 | if delta != dimensions * (0,): 21 | neighbour_counts[tuple(map(add, cell, delta))] += 1 22 | 23 | for cell, count in neighbour_counts.items(): 24 | if count == 3 or (cell in board and count == 2): 25 | new_board.add(cell) 26 | board = new_board 27 | 28 | return len(board) 29 | 30 | with open('input.txt') as file: 31 | lines = file.read().splitlines() 32 | 33 | print(solve(lines, 6, 3)) 34 | print(solve(lines, 6, 4)) 35 | -------------------------------------------------------------------------------- /2020/18/18.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | import ast, operator 3 | 4 | with open('in.txt') as f: 5 | lines = f.read().splitlines() 6 | 7 | def visit(node): 8 | contents = [] 9 | for _, value in ast.iter_fields(node): 10 | if isinstance(value, ast.AST): 11 | value = [value] 12 | if isinstance(value, list): 13 | contents.extend(visit(item) for item in value) 14 | 15 | return defaultdict(lambda: lambda: None, { 16 | 'Constant': lambda: node.value, 17 | 'Expr': lambda: contents[0], 18 | 'Module': lambda: contents[0], 19 | 'BinOp': lambda: { 20 | 'Add': operator.add, 21 | 'Sub': operator.mul 22 | }[type(node.op).__name__](contents[0], contents[2]) 23 | })[type(node).__name__]() 24 | 25 | values = [] 26 | for line in lines: 27 | line = line.translate(''.maketrans({'*': '-'})) 28 | values.append(visit(ast.parse(line))) 29 | 30 | print(sum(values)) 31 | -------------------------------------------------------------------------------- /2020/18/18b.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | import ast, operator 3 | 4 | with open('in.txt') as f: 5 | lines = f.read().splitlines() 6 | 7 | def visit(node): 8 | contents = [] 9 | for _, value in ast.iter_fields(node): 10 | if isinstance(value, ast.AST): 11 | value = [value] 12 | if isinstance(value, list): 13 | contents.extend(visit(item) for item in value) 14 | 15 | return defaultdict(lambda: lambda: None, { 16 | 'Constant': lambda: node.value, 17 | 'Expr': lambda: contents[0], 18 | 'Module': lambda: contents[0], 19 | 'BinOp': lambda: { 20 | 'Add': operator.mul, 21 | 'Mult': operator.add 22 | }[type(node.op).__name__](contents[0], contents[2]) 23 | })[type(node).__name__]() 24 | 25 | values = [] 26 | for line in lines: 27 | line = line.translate(''.maketrans({'+': '*', '*': '+'})) 28 | values.append(visit(ast.parse(line))) 29 | 30 | print(sum(values)) 31 | -------------------------------------------------------------------------------- /2020/19/19.py: -------------------------------------------------------------------------------- 1 | from lark import Lark 2 | import re 3 | 4 | with open('in.txt') as f: 5 | rules, inputs = f.read().split('\n\n') 6 | 7 | grammar = ['start: s0'] 8 | for line in rules.splitlines(): 9 | grammar.append(re.sub(r'(\d+)', r's\1', line)) 10 | grammar = '\n'.join(grammar) 11 | 12 | l = Lark(grammar) 13 | cnt = 0 14 | for input in inputs.splitlines(): 15 | try: 16 | l.parse(input) 17 | cnt += 1 18 | except: 19 | pass 20 | 21 | print(cnt) 22 | -------------------------------------------------------------------------------- /2020/2/2.py: -------------------------------------------------------------------------------- 1 | from parse import parse 2 | from collections import Counter 3 | 4 | with open('input.txt') as f: 5 | lines = f.read().splitlines() 6 | 7 | valid = 0 8 | for line in lines: 9 | low, high, letter, password = parse('{:d}-{:d} {:w}: {:w}', line) 10 | counter = Counter(password) 11 | if low <= counter[letter] <= high: 12 | valid += 1 13 | print(valid) 14 | -------------------------------------------------------------------------------- /2020/2/2b.py: -------------------------------------------------------------------------------- 1 | from parse import parse 2 | 3 | with open('input2.txt') as f: 4 | lines = f.read().splitlines() 5 | 6 | valid = 0 7 | for line in lines: 8 | low, high, letter, password = parse('{:d}-{:d} {:w}: {:w}', line) 9 | if (password[low - 1] == letter) != (password[high - 1] == letter): 10 | valid += 1 11 | print(valid) 12 | -------------------------------------------------------------------------------- /2020/20/20.py: -------------------------------------------------------------------------------- 1 | from collections import Counter, defaultdict 2 | 3 | with open('in.txt') as f: 4 | tiles = list(filter(lambda x: x != '', f.read().split('\n\n'))) 5 | 6 | class Tile: 7 | def __init__(self, id, tile): 8 | self.id = id 9 | self.borders = [ 10 | tuple(tile[0]), 11 | tuple(tuple(zip(*tile))[-1]), 12 | tuple(tile[-1]), 13 | tuple(tuple(zip(*tile))[0]) 14 | ] 15 | self.neighbours = 4 * [None] 16 | self.position = None 17 | 18 | assert Counter(self.borders + list(map(lambda x: x[::-1], self.borders))).most_common(1)[0][1] == 1 19 | 20 | def vflip(self): 21 | self.borders[0], self.borders[2] = self.borders[2], self.borders[0] 22 | self.borders[1] = self.borders[1][::-1] 23 | self.borders[3] = self.borders[3][::-1] 24 | 25 | def hflip(self): 26 | self.borders[1], self.borders[3] = self.borders[3], self.borders[1] 27 | self.borders[0] = self.borders[0][::-1] 28 | self.borders[2] = self.borders[2][::-1] 29 | 30 | def rotate(self): 31 | self.borders[0], self.borders[1], self.borders[2], self.borders[3] =\ 32 | self.borders[3], self.borders[0], self.borders[1], self.borders[2] 33 | self.borders[2] = self.borders[2][::-1] 34 | self.borders[0] = self.borders[0][::-1] 35 | 36 | def transform(self): 37 | for hflip in 0, 1: 38 | if hflip: 39 | self.hflip() 40 | for vflip in 0, 1: 41 | if vflip: 42 | self.vflip() 43 | for _ in range(4): 44 | self.rotate() 45 | yield self 46 | if vflip: 47 | self.vflip() 48 | if hflip: 49 | self.hflip() 50 | 51 | def satisfies(self, constraint): 52 | side, border = constraint 53 | return self.borders[side] == border 54 | 55 | bordermap = defaultdict(set) 56 | all_tiles = [] 57 | for tile in tiles: 58 | tile = tile.splitlines() 59 | id = int(tile[0].split(' ')[1][:-1]) 60 | tile = list(map(list, tile[1:])) 61 | tile = Tile(id, tile) 62 | all_tiles.append(tile) 63 | for _ in tile.transform(): 64 | for border in tile.borders: 65 | bordermap[border].add(tile) 66 | 67 | singletonscount = Counter() 68 | 69 | for border, tileset in bordermap.items(): 70 | if len(tileset) == 1: 71 | singletonscount[list(tileset)[0].id] += 1 72 | 73 | corners = singletonscount.most_common(4) 74 | mul = 1 75 | for corner, _ in corners: 76 | mul *= corner 77 | print(mul) 78 | -------------------------------------------------------------------------------- /2020/21/21.py: -------------------------------------------------------------------------------- 1 | from collections import Counter, defaultdict 2 | 3 | with open('in.txt') as f: 4 | lines = f.read().splitlines() 5 | 6 | allergen_to_ingsets = defaultdict(list) 7 | ingredient_count = Counter() 8 | 9 | for line in lines: 10 | ingredients, allergens = line.split(' (contains ') 11 | ingredients = set(ingredients.split(' ')) 12 | ingredient_count += Counter(ingredients) 13 | allergens = allergens[:-1].split(', ') 14 | for allergen in allergens: 15 | allergen_to_ingsets[allergen].append(ingredients) 16 | 17 | solution = {} 18 | 19 | while True: 20 | for allergen, setlist in allergen_to_ingsets.items(): 21 | intersection = set.intersection(*setlist) 22 | if len(intersection) == 1: 23 | ingredient = intersection.pop() 24 | solution[allergen] = ingredient 25 | for _, setlist in allergen_to_ingsets.items(): 26 | for ingredientset in setlist: 27 | ingredientset.discard(ingredient) 28 | break 29 | else: 30 | break 31 | 32 | good_ingredients = set.union(*[set.union(*ingsets) for ingsets in allergen_to_ingsets.values()]) 33 | cnt = 0 34 | for ingredient in good_ingredients: 35 | cnt += ingredient_count[ingredient] 36 | print(cnt) 37 | 38 | solution = sorted(solution.items()) 39 | print(','.join([ingredient for _, ingredient in solution])) 40 | -------------------------------------------------------------------------------- /2020/22/22.py: -------------------------------------------------------------------------------- 1 | from collections import deque 2 | from itertools import count 3 | 4 | with open('in.txt') as f: 5 | players = f.read().split('\n\n') 6 | 7 | players[0] = players[0].splitlines()[1:] 8 | players[1] = players[1].splitlines()[1:] 9 | players[0] = deque(map(int, players[0])) 10 | players[1] = deque(map(int, players[1])) 11 | 12 | def move(players): 13 | cards = players[0].popleft(), players[1].popleft() 14 | if cards[0] > cards[1]: 15 | winner = 0 16 | else: 17 | winner = 1 18 | players[winner].append(cards[winner]) 19 | players[winner].append(cards[1 - winner]) 20 | 21 | while players[0] and players[1]: 22 | move(players) 23 | 24 | def score(player): 25 | return sum(x * y for x, y in zip(count(1), list(player)[::-1])) 26 | 27 | if players[0]: 28 | winner = 1 29 | else: 30 | winner = 0 31 | print('winner:', winner) 32 | print(sum(map(score, players))) 33 | -------------------------------------------------------------------------------- /2020/22/22b.py: -------------------------------------------------------------------------------- 1 | from collections import deque 2 | from itertools import count 3 | 4 | with open('in.txt') as f: 5 | players = f.read().split('\n\n') 6 | 7 | players[0] = players[0].splitlines()[1:] 8 | players[1] = players[1].splitlines()[1:] 9 | players[0] = deque(map(int, players[0])) 10 | players[1] = deque(map(int, players[1])) 11 | 12 | memo = {} 13 | 14 | def game(players): 15 | conf = (tuple(players[0]), tuple(players[1])) 16 | try: 17 | return memo[conf] 18 | except: 19 | pass 20 | 21 | seen = set() 22 | 23 | while players[0] and players[1]: 24 | prev = (tuple(players[0]), tuple(players[1])) 25 | if prev in seen: 26 | memo[conf] = 0 27 | return 0 28 | seen.add(prev) 29 | move(players) 30 | 31 | if players[0]: 32 | memo[conf] = 0 33 | return 0 34 | memo[conf] = 1 35 | return 1 36 | 37 | def move(players): 38 | cards = players[0].popleft(), players[1].popleft() 39 | if cards[0] <= len(players[0]) and cards[1] <= len(players[1]): 40 | # recurse 41 | winner = game([ 42 | deque(list(players[0])[:cards[0]]), 43 | deque(list(players[1])[:cards[1]]) 44 | ]) 45 | else: 46 | if cards[0] > cards[1]: 47 | winner = 0 48 | else: 49 | winner = 1 50 | players[winner].append(cards[winner]) 51 | players[winner].append(cards[1 - winner]) 52 | return winner 53 | 54 | def score(player): 55 | return sum(x * y for x, y in zip(count(1), list(player)[::-1])) 56 | 57 | winner = game(players) 58 | print('winner:', winner) 59 | print(score(players[winner])) 60 | -------------------------------------------------------------------------------- /2020/23/23.py: -------------------------------------------------------------------------------- 1 | with open('in.txt') as f: 2 | state = list(map(int, list(f.read().strip()))) 3 | 4 | MOVES = 100 5 | current = 0 6 | for _ in range(MOVES): 7 | cur_val = state[current] 8 | 9 | popidx = current + 1 10 | if popidx >= len(state): 11 | popidx %= len(state) 12 | current -= 1 13 | x = state.pop(popidx) 14 | popidx = current + 1 15 | if popidx >= len(state): 16 | popidx %= len(state) 17 | current -= 1 18 | y = state.pop(popidx) 19 | popidx = current + 1 20 | if popidx >= len(state): 21 | popidx %= len(state) 22 | current -= 1 23 | z = state.pop(popidx) 24 | 25 | dest = cur_val - 1 26 | if dest < min(state): 27 | dest = max(state) 28 | while dest in (x, y, z): 29 | dest -= 1 30 | if dest < min(state): 31 | dest = max(state) 32 | dest = state.index(dest) 33 | state.insert(dest + 1, x) 34 | state.insert(dest + 2, y) 35 | state.insert(dest + 3, z) 36 | if dest < current: 37 | current += 3 38 | current = (current + 1) % len(state) 39 | 40 | out = '' 41 | idx = state.index(1) 42 | for i, _ in enumerate(state): 43 | out += str(state[(idx + i + 1) % len(state)]) 44 | 45 | print(out[:-1]) 46 | -------------------------------------------------------------------------------- /2020/23/23b2.py: -------------------------------------------------------------------------------- 1 | from sortedcontainers import SortedSet 2 | 3 | MAX_CUP = 1000000 4 | MAX_MOVES = 10000000 5 | INPUT = '586439172' 6 | 7 | class Node: 8 | def __init__(self, value): 9 | self.value = value 10 | self.next = None 11 | self.prev = None 12 | 13 | def insert_after(self, node): 14 | node.next = self.next 15 | self.next.prev = node 16 | self.next = node 17 | node.prev = self 18 | 19 | def remove(self): 20 | self.prev.next, self.next.prev = self.next, self.prev 21 | 22 | state = list(map(int, INPUT)) 23 | 24 | prev = None 25 | begin = None 26 | node = None 27 | node_by_value = {} 28 | 29 | for num in state + list(range(max(state) + 1, MAX_CUP + 1)): 30 | node = Node(num) 31 | if prev is None: 32 | begin = node 33 | else: 34 | prev.next = node 35 | node.prev = prev 36 | prev = node 37 | node_by_value[num] = node 38 | 39 | node.next = begin 40 | begin.prev = node 41 | 42 | cur = begin 43 | cups = SortedSet(node_by_value.keys()) 44 | 45 | for i in range(MAX_MOVES): 46 | next = cur.next 47 | x = next 48 | next.remove() 49 | next = next.next 50 | y = next 51 | next.remove() 52 | next = next.next 53 | z = next 54 | next.remove() 55 | 56 | cups.remove(x.value) 57 | cups.remove(y.value) 58 | cups.remove(z.value) 59 | 60 | dest = cur.value - 1 61 | minimum = cups[0] 62 | maximum = cups[-1] 63 | 64 | if dest < minimum: 65 | dest = maximum 66 | while dest in (x.value, y.value, z.value): 67 | dest -= 1 68 | if dest < minimum: 69 | dest = maximum 70 | 71 | dest_it = node_by_value[dest] 72 | dest_it.insert_after(x) 73 | x.insert_after(y) 74 | y.insert_after(z) 75 | 76 | cups.add(x.value) 77 | cups.add(y.value) 78 | cups.add(z.value) 79 | 80 | cur = cur.next 81 | 82 | print(node_by_value[1].next.value * node_by_value[1].next.next.value) 83 | -------------------------------------------------------------------------------- /2020/24/24.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict, Counter 2 | 3 | with open('input.txt') as f: 4 | lines = f.read().splitlines() 5 | 6 | deltas = { 7 | 'w': (0, -2), 8 | 'e': (0, 2), 9 | 'nw': (-1, -1), 10 | 'se': (1, 1), 11 | 'ne': (-1, 1), 12 | 'sw': (1, -1) 13 | } 14 | 15 | def pos(tile): 16 | i = 0 17 | row, col = 0, 0 18 | while i < len(tile): 19 | try: 20 | delta = deltas[tile[i]] 21 | except: 22 | delta = deltas[tile[i:i + 2]] 23 | i += 1 24 | row += delta[0] 25 | col += delta[1] 26 | i += 1 27 | return row, col 28 | 29 | def evolve(floor): 30 | cnt_neighbours = Counter() 31 | 32 | for pos, cell in floor.items(): 33 | cnt_neighbours[pos] += 0 34 | if cell: 35 | for dy, dx in deltas.values(): 36 | neighbour = pos[0] + dy, pos[1] + dx 37 | cnt_neighbours[neighbour] += 1 38 | 39 | new_floor = floor.copy() 40 | for pos, cnt in cnt_neighbours.items(): 41 | if floor[pos] and cnt == 0 or cnt > 2: 42 | new_floor[pos] = False 43 | if not floor[pos] and cnt == 2: 44 | new_floor[pos] = True 45 | 46 | return new_floor 47 | 48 | floor = defaultdict(bool) 49 | for tile in lines: 50 | position = pos(tile) 51 | floor[position] = not floor[position] 52 | 53 | print(list(floor.values()).count(True)) 54 | DAYS = 100 55 | for _ in range(DAYS): 56 | floor = evolve(floor) 57 | print(list(floor.values()).count(True)) 58 | -------------------------------------------------------------------------------- /2020/25/25.py: -------------------------------------------------------------------------------- 1 | with open('in.txt') as f: 2 | pub = list(map(int, f.read().splitlines())) 3 | 4 | MOD = 20201227 5 | GENERATOR = 7 6 | 7 | def pub2priv(pub): 8 | prod = 1 9 | for priv in range(0, MOD): 10 | if prod == pub: 11 | return priv 12 | prod *= GENERATOR 13 | prod %= MOD 14 | return None 15 | 16 | priv0 = pub2priv(pub[0]) 17 | print(pow(pub[1], priv0, MOD)) 18 | -------------------------------------------------------------------------------- /2020/3/3.py: -------------------------------------------------------------------------------- 1 | with open('in.txt') as f: 2 | lines = f.read().splitlines() 3 | 4 | def trees(slope_y, slope_x): 5 | trees = 0 6 | x = 0 7 | for y in range(0, len(lines), slope_x): 8 | line = lines[y] 9 | trees += line[x % len(line)] == '#' 10 | x += slope_y 11 | return trees 12 | 13 | print(trees(3, 1)) 14 | 15 | p = 1 16 | for slope_y, slope_x in ((1, 1), (3, 1), (5, 1), (7, 1), (1, 2)): 17 | p *= trees(slope_y, slope_x) 18 | print(p) 19 | -------------------------------------------------------------------------------- /2020/4/4.py: -------------------------------------------------------------------------------- 1 | with open('in.txt') as f: 2 | lines = f.read().splitlines() 3 | 4 | password = {} 5 | count = 0 6 | for line in lines: 7 | if line == '': 8 | valid = (len(password) == 8) or (len(password) == 7 and 'cid' not in password) 9 | count += valid 10 | password = {} 11 | else: 12 | data = line.split(' ') 13 | for word in data: 14 | key, value = word.split(':') 15 | password[key] = value 16 | 17 | print(count) 18 | -------------------------------------------------------------------------------- /2020/4/4b.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | with open('in.txt') as f: 4 | input = f.read().split('\n\n') 5 | 6 | rules = { 7 | 'byr': lambda x: 1920 <= int(x) <= 2002, 8 | 'iyr': lambda x: 2010 <= int(x) <= 2020, 9 | 'eyr': lambda x: 2020 <= int(x) <= 2030, 10 | 'hgt': lambda x: x[-2:] == 'cm' and 150 <= int(x[:-2]) <= 193 or\ 11 | x[-2:] == 'in' and 59 <= int(x[:-2]) <= 76, 12 | 'hcl': lambda x: re.match(r'^\#[0-9a-f]{6}$', x), 13 | 'ecl': lambda x: x in ('amb', 'blu', 'brn', 'gry', 'grn', 'hzl', 'oth'), 14 | 'pid': lambda x: re.match(r'^[0-9]{9}$', x) 15 | } 16 | 17 | def valid(passport): 18 | for key in rules: 19 | if key not in passport or not rules[key](passport[key]): 20 | return False 21 | return True 22 | 23 | count = 0 24 | for data in input: 25 | fields = data.split() 26 | passport = {} 27 | for field in fields: 28 | key, value = field.split(':') 29 | passport[key] = value 30 | count += valid(passport) 31 | 32 | print(count) 33 | -------------------------------------------------------------------------------- /2020/5/5.py: -------------------------------------------------------------------------------- 1 | with open('in.txt') as f: 2 | lines = f.read().splitlines() 3 | 4 | def pos(ticket): 5 | row, col = ticket[:7], ticket[7:] 6 | row = row.replace('F', '0').replace('B', '1') 7 | col = col.replace('L', '0').replace('R', '1') 8 | 9 | return int(row, 2), int(col, 2) 10 | 11 | def id(row, col): 12 | return 8 * row + col 13 | 14 | rows = set() 15 | cols = set() 16 | ids = set() 17 | 18 | for line in lines: 19 | row, col = pos(line) 20 | rows.add(row) 21 | cols.add(col) 22 | ids.add(id(row, col)) 23 | 24 | print(max(ids)) 25 | 26 | for col in cols: 27 | for row in rows: 28 | myid = id(row, col) 29 | if myid not in ids and myid + 1 in ids and myid - 1 in ids: 30 | print(myid) 31 | -------------------------------------------------------------------------------- /2020/6/6.py: -------------------------------------------------------------------------------- 1 | with open('in.txt') as f: 2 | lines = f.read().split('\n\n') 3 | 4 | anyone = [] 5 | everyone = [] 6 | for group in lines: 7 | people = list(map(set, group.splitlines())) 8 | anyone.append(len(set.union(*people))) 9 | everyone.append(len(set.intersection(*people))) 10 | 11 | print(sum(anyone)) 12 | print(sum(everyone)) 13 | -------------------------------------------------------------------------------- /2020/7/7.py: -------------------------------------------------------------------------------- 1 | import re 2 | from collections import defaultdict, Counter 3 | 4 | with open('in.txt') as f: 5 | lines = f.read().splitlines() 6 | 7 | G1 = defaultdict(list) 8 | G2 = defaultdict(list) 9 | for line in lines: 10 | child, parents = line.split(' bags contain ') 11 | if not parents.startswith('no other'): 12 | for parent in parents.split(', '): 13 | count, parent = re.match(r'(\d+) (.*) bag', parent).group(1, 2) 14 | G1[parent].append((child, int(count))) 15 | G2[child].append((parent, int(count))) 16 | 17 | def search(G, root): 18 | q = [(root, 1)] 19 | visited = Counter() 20 | while q: 21 | item, multiplier = q.pop() 22 | visited[item] += multiplier 23 | for child, weight in G[item]: 24 | q.append((child, weight * multiplier)) 25 | return visited 26 | 27 | root = 'shiny gold' 28 | print(len(search(G1, root)) - 1) 29 | print(sum(search(G2, root).values()) - 1) 30 | -------------------------------------------------------------------------------- /2020/8/8.py: -------------------------------------------------------------------------------- 1 | from parse import parse 2 | 3 | with open('in.txt') as f: 4 | lines = f.read().splitlines() 5 | 6 | visited = set() 7 | acc = ip = 0 8 | while ip not in visited: 9 | visited.add(ip) 10 | opcode, val = parse('{:w} {:d}', lines[ip]) 11 | if opcode == 'acc': 12 | acc += val 13 | elif opcode == 'jmp': 14 | ip += val 15 | continue 16 | ip += 1 17 | print(acc) 18 | -------------------------------------------------------------------------------- /2020/8/8b.py: -------------------------------------------------------------------------------- 1 | from parse import parse 2 | 3 | with open('in.txt') as f: 4 | lines = f.read().splitlines() 5 | 6 | def run(lines): 7 | visited = set() 8 | acc = ip = 0 9 | while ip not in visited: 10 | visited.add(ip) 11 | if ip >= len(lines): 12 | return acc 13 | if ip < 0: 14 | break 15 | opcode, val = parse('{:w} {:d}', lines[ip]) 16 | if opcode == 'acc': 17 | acc += val 18 | elif opcode == 'jmp': 19 | ip += val 20 | continue 21 | ip += 1 22 | return None 23 | 24 | for i, line in enumerate(lines): 25 | opcode, val = line.split(' ') 26 | if opcode == 'jmp': 27 | lines[i] = 'nop ' + val 28 | elif opcode == 'nop': 29 | lines[i] = 'jmp ' + val 30 | else: 31 | continue 32 | acc = run(lines) 33 | if acc is not None: 34 | print(acc) 35 | break 36 | lines[i] = line 37 | -------------------------------------------------------------------------------- /2020/9/9.py: -------------------------------------------------------------------------------- 1 | from collections import Counter 2 | from itertools import permutations 3 | 4 | with open('in.txt') as f: 5 | lines = list(map(int, f.read().splitlines())) 6 | 7 | PREAMBLE = 25 8 | for i, num in enumerate(lines[PREAMBLE:]): 9 | sums = map(sum, permutations(set(lines[i:i+PREAMBLE]), r=2)) 10 | if num not in sums: 11 | target = num 12 | break 13 | 14 | print(target) 15 | 16 | for l in range(2, len(lines) + 1): 17 | s = Counter(lines[:l]) 18 | window = sum(lines[:l]) 19 | for next, prev in zip(lines[l:], lines[:-l]): 20 | window += next - prev 21 | s[next] += 1 22 | s[prev] -= 1 23 | if window == target: 24 | s += Counter() 25 | print(min(s.keys()) + max(s.keys())) 26 | break 27 | -------------------------------------------------------------------------------- /2021/1/1.py: -------------------------------------------------------------------------------- 1 | with open('in.txt') as f: 2 | nums = list(map(int, f.read().splitlines())) 3 | 4 | increases = [x > y for x, y in zip(nums[1:], nums)] 5 | print(sum(increases)) 6 | -------------------------------------------------------------------------------- /2021/1/1b.py: -------------------------------------------------------------------------------- 1 | with open('in.txt') as f: 2 | nums = list(map(int, f.read().splitlines())) 3 | 4 | windows = list(map(sum, zip(nums, nums[1:], nums[2:]))) 5 | increases = [x - y > 0 for (x, y) in zip(windows[1:], windows)] 6 | print(sum(increases)) 7 | -------------------------------------------------------------------------------- /2021/10/10.py: -------------------------------------------------------------------------------- 1 | with open('in.txt') as f: 2 | lines = f.read().splitlines() 3 | 4 | inv = {')': '(', '}': '{', ']': '[', '>': '<'} 5 | score = {')': 3, ']': 57, '}': 1197, '>': 25137} 6 | 7 | def error(line): 8 | s = [] 9 | for char in line: 10 | if char in ('(', '{', '[', '<'): 11 | s.append(char) 12 | elif len(s) == 0 or s.pop() != inv[char]: 13 | return score[char] 14 | return 0 15 | 16 | print(sum(map(error, lines))) 17 | -------------------------------------------------------------------------------- /2021/10/10b.py: -------------------------------------------------------------------------------- 1 | with open('in.txt') as f: 2 | lines = f.read().splitlines() 3 | 4 | inv = {')': '(', '}': '{', ']': '[', '>': '<'} 5 | score = {'(': 1, '[': 2, '{': 3, '<': 4} 6 | 7 | def error(line): 8 | s = [] 9 | for char in line: 10 | if char in ('(', '{', '[', '<'): 11 | s.append(char) 12 | elif len(s) == 0 or s.pop() != inv[char]: 13 | return None 14 | return s 15 | 16 | values = [] 17 | for line in lines: 18 | s = error(line) 19 | if s is not None: 20 | value = 0 21 | for t in s[::-1]: 22 | value *= 5 23 | value += score[t] 24 | values.append(value) 25 | 26 | print(sorted(values)[len(values)//2]) 27 | -------------------------------------------------------------------------------- /2021/11/11.py: -------------------------------------------------------------------------------- 1 | with open('in.txt') as f: 2 | board = [list(map(int, line)) for line in f.read().splitlines()] 3 | 4 | def step(board): 5 | def flood(board, x, y): 6 | board[y][x] += 1 7 | 8 | if board[y][x] != 10: 9 | return 0 10 | 11 | cnt = 1 12 | for dx in (-1, 0, 1): 13 | for dy in (-1, 0, 1): 14 | if dx == 0 and dy == 0: 15 | continue 16 | if 0 <= x + dx < len(board[0]) and 0 <= y + dy < len(board): 17 | cnt += flood(board, x + dx, y + dy) 18 | return cnt 19 | 20 | cnt = 0 21 | for y, line in enumerate(board): 22 | for x, cell in enumerate(line): 23 | cnt += flood(board, x, y) 24 | 25 | board = [[cell if cell <= 9 else 0 for cell in line] for line in board] 26 | 27 | return board, cnt 28 | 29 | cnt = 0 30 | N = 100 31 | for i in range(N): 32 | board, flashes = step(board) 33 | cnt += flashes 34 | 35 | print(cnt) 36 | -------------------------------------------------------------------------------- /2021/11/11b.py: -------------------------------------------------------------------------------- 1 | from pprint import pprint 2 | from itertools import count 3 | 4 | with open('in.txt') as f: 5 | board = [list(map(int, line)) for line in f.read().splitlines()] 6 | 7 | def step(board): 8 | def flood(board, x, y): 9 | board[y][x] += 1 10 | 11 | if board[y][x] != 10: 12 | return 0 13 | 14 | cnt = 1 15 | for dx in (-1, 0, 1): 16 | for dy in (-1, 0, 1): 17 | if dx == 0 and dy == 0: 18 | continue 19 | if 0 <= x + dx < len(board[0]) and 0 <= y + dy < len(board): 20 | cnt += flood(board, x + dx, y + dy) 21 | return cnt 22 | 23 | cnt = 0 24 | for y, line in enumerate(board): 25 | for x, cell in enumerate(line): 26 | cnt += flood(board, x, y) 27 | 28 | board = [[cell if cell <= 9 else 0 for cell in line] for line in board] 29 | 30 | return (board, cnt) 31 | 32 | target = sum([sum([1 for cell in line]) for line in board]) 33 | 34 | for i in count(1): 35 | board, flashes = step(board) 36 | if flashes == target: 37 | print(i) 38 | break 39 | -------------------------------------------------------------------------------- /2021/12/12.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | 3 | with open('in.txt') as f: 4 | lines = f.read().splitlines() 5 | 6 | G = defaultdict(set) 7 | 8 | for line in lines: 9 | start, end = line.split('-') 10 | G[start].add(end) 11 | G[end].add(start) 12 | 13 | def paths(G, start, path): 14 | if start == 'end': 15 | yield 16 | 17 | for neighbor in G[start]: 18 | if neighbor.isupper() or neighbor not in path: 19 | yield from paths(G, neighbor, path + [neighbor]) 20 | 21 | print(len(list(paths(G, 'start', ['start'])))) 22 | -------------------------------------------------------------------------------- /2021/12/12b.py: -------------------------------------------------------------------------------- 1 | from collections import Counter, defaultdict 2 | 3 | with open('in.txt') as f: 4 | lines = f.read().splitlines() 5 | 6 | G = defaultdict(set) 7 | 8 | for line in lines: 9 | start, end = line.split('-') 10 | G[start].add(end) 11 | G[end].add(start) 12 | 13 | def good(path): 14 | most_visited = path.most_common(2) 15 | return most_visited[0][1] + most_visited[1][1] <= 3 16 | 17 | def paths(G, start, path): 18 | for neighbor in G[start]: 19 | if neighbor == 'end': 20 | yield 21 | continue 22 | if neighbor == 'start': 23 | continue 24 | new_path = Counter(path) 25 | if neighbor.islower(): 26 | new_path += Counter([neighbor]) 27 | if neighbor.isupper() or good(new_path): 28 | yield from paths(G, neighbor, new_path) 29 | 30 | print(len(list(paths(G, 'start', Counter(['start']))))) 31 | -------------------------------------------------------------------------------- /2021/13/13.py: -------------------------------------------------------------------------------- 1 | with open('in.txt') as f: 2 | dots, instructions = f.read().split('\n\n') 3 | 4 | dots = dots.split('\n') 5 | instructions = instructions.split('\n')[:-1] 6 | 7 | board = set() 8 | for dot in dots: 9 | board.add(map(int, dot.split(',')) 10 | 11 | for instruction in instructions: 12 | instruction = instruction.split('fold along ')[1] 13 | direction, location = instruction.split('=') 14 | direction = { 'x': 0, 'y': 1 }[direction] 15 | location = int(location) 16 | 17 | new_board = set() 18 | for dot in board: 19 | dot = list(dot) 20 | if dot[direction] > location: 21 | dot[direction] = 2*location - dot[direction] 22 | new_board.add(tuple(dot)) 23 | board = new_board 24 | break 25 | 26 | print(len(new_board)) 27 | -------------------------------------------------------------------------------- /2021/13/13b.py: -------------------------------------------------------------------------------- 1 | with open('in.txt') as f: 2 | dots, instructions = f.read().split('\n\n') 3 | 4 | dots = dots.split('\n') 5 | instructions = instructions.split('\n')[:-1] 6 | 7 | board = set() 8 | for dot in dots: 9 | board.add(map(int, dot.split(','))) 10 | 11 | def print_board(board): 12 | max_x = max([x for x, y in board]) 13 | max_y = max([y for x, y in board]) 14 | 15 | for y in range(max_y + 1): 16 | for x in range(max_x + 1): 17 | if (x, y) in board: 18 | print('#', end='') 19 | else: 20 | print('.', end='') 21 | print() 22 | print() 23 | 24 | for instruction in instructions: 25 | instruction = instruction.split('fold along ')[1] 26 | direction, location = instruction.split('=') 27 | direction = { 'x': 0, 'y': 1 }[direction] 28 | location = int(location) 29 | 30 | new_board = set() 31 | for dot in board: 32 | dot = list(dot) 33 | if dot[direction] > location: 34 | dot[direction] = 2*location - dot[direction] 35 | new_board.add(tuple(dot)) 36 | board = new_board 37 | 38 | print_board(board) 39 | -------------------------------------------------------------------------------- /2021/14/14.py: -------------------------------------------------------------------------------- 1 | from collections import Counter 2 | 3 | with open('in.txt') as f: 4 | template, rules = f.read().split('\n\n') 5 | rules = rules.split('\n')[:-1] 6 | 7 | def step(str): 8 | ret = '' 9 | for i in range(len(str) - 1): 10 | lookup = str[i:i + 2] 11 | ret += str[i] + apply[lookup] 12 | ret += str[-1] 13 | return ret 14 | 15 | apply = {} 16 | for rule in rules: 17 | left, right = rule.split(' -> ') 18 | apply[left] = right 19 | 20 | for i in range(10): 21 | template = step(template) 22 | 23 | z = Counter(template) 24 | x = z.most_common() 25 | print(x[0][1] - x[-1][1]) 26 | -------------------------------------------------------------------------------- /2021/14/14b.py: -------------------------------------------------------------------------------- 1 | from collections import Counter 2 | from itertools import pairwise 3 | 4 | with open('in.txt') as f: 5 | template, rules = f.read().split('\n\n') 6 | rules = rules.split('\n')[:-1] 7 | 8 | def step(parts): 9 | new_parts = Counter() 10 | for part, cnt in parts.items(): 11 | mid = apply[part] 12 | new_parts[(part[0], mid)] += cnt 13 | new_parts[(mid, part[1])] += cnt 14 | return new_parts 15 | 16 | apply = {} 17 | for rule in rules: 18 | left, right = rule.split(' -> ') 19 | apply[tuple(left)] = right 20 | 21 | parts = Counter(pairwise(template)) 22 | for _ in range(40): 23 | parts = step(parts) 24 | 25 | final = Counter([template[0], template[-1]]) 26 | for (a, b), cnt in parts.items(): 27 | final[a] += cnt 28 | final[b] += cnt 29 | 30 | x = final.most_common() 31 | print(x[0][1]//2 - x[-1][1]//2) 32 | -------------------------------------------------------------------------------- /2021/15/15.py: -------------------------------------------------------------------------------- 1 | from heapq import heappop, heappush 2 | 3 | with open('in.txt') as f: 4 | board = [[int(cell) for cell in line] for line in f.read().splitlines()] 5 | 6 | def dijkstra(board, start, end): 7 | (sx, sy) = start 8 | visited = set() 9 | frontier = [(0, sx, sy)] 10 | 11 | while frontier: 12 | cost, x, y = heappop(frontier) 13 | if (x, y) in visited: 14 | continue 15 | if (x, y) == end: 16 | return cost 17 | visited.add((x, y)) 18 | 19 | for dx, dy in (0, -1), (0, 1), (-1, 0), (1, 0): 20 | if 0 <= x + dx < len(board[0]) and 0 <= y + dy < len(board) and (x + dx, y + dy) not in visited: 21 | heappush(frontier, (cost + board[y + dy][x + dx], x + dx, y + dy)) 22 | 23 | return None 24 | 25 | print(dijkstra(board, (0, 0), (len(board[0]) - 1, len(board) - 1))) 26 | -------------------------------------------------------------------------------- /2021/15/15b.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from heapq import heappop, heappush 3 | 4 | with open('small.txt') as f: 5 | tile = np.array([[int(cell) for cell in line] for line in f.read().splitlines()]) 6 | 7 | board = [] 8 | for y in range(5): 9 | row = [] 10 | for x in range(5): 11 | next_tile = (tile + (x + y)) % 9 12 | next_tile = np.where(next_tile == 0, 9, next_tile) 13 | row.append(next_tile) 14 | board.append(row) 15 | board = np.block(board) 16 | 17 | def dijkstra(board, start, end): 18 | (sx, sy) = start 19 | visited = set() 20 | frontier = [(0, sx, sy)] 21 | 22 | while frontier: 23 | cost, x, y = heappop(frontier) 24 | if (x, y) in visited: 25 | continue 26 | if (x, y) == end: 27 | return cost 28 | visited.add((x, y)) 29 | 30 | for dx, dy in (0, -1), (0, 1), (-1, 0), (1, 0): 31 | if 0 <= x + dx < len(board[0]) and 0 <= y + dy < len(board) and (x + dx, y + dy) not in visited: 32 | heappush(frontier, (cost + board[y + dy][x + dx], x + dx, y + dy)) 33 | 34 | return None 35 | 36 | print(dijkstra(board, (0, 0), (len(board[0]) - 1, len(board) - 1))) 37 | -------------------------------------------------------------------------------- /2021/16/16.py: -------------------------------------------------------------------------------- 1 | with open('in.txt') as f: 2 | hex_packets = f.read().strip() 3 | packets = ''.join([bin(int(c, 16))[2:].zfill(4) for c in hex_packets]) 4 | 5 | def parse(packets): 6 | def consume(n): 7 | nonlocal packets 8 | data, packets = packets[:n], packets[n:] 9 | return data 10 | version = int(consume(3), 2) 11 | T = int(consume(3), 2) 12 | if T == 4: 13 | while True: 14 | literal = consume(5) 15 | if literal[0] == '0': 16 | return version, packets 17 | I = int(consume(1), 2) 18 | if I == 0: 19 | L = int(consume(15), 2) 20 | subpackets = consume(L) 21 | while subpackets: 22 | ret_ver, subpackets = parse(subpackets) 23 | version += ret_ver 24 | elif I == 1: 25 | L = int(consume(11), 2) 26 | for _ in range(L): 27 | ret_ver, packets = parse(packets) 28 | version += ret_ver 29 | return version, packets 30 | 31 | print(parse(packets)) 32 | -------------------------------------------------------------------------------- /2021/16/16b.py: -------------------------------------------------------------------------------- 1 | from operator import mul, lt, gt, eq 2 | from functools import reduce, partial 3 | 4 | with open('in.txt') as f: 5 | hex_packets = f.read().strip() 6 | packets = ''.join([bin(int(c, 16))[2:].zfill(4) for c in hex_packets]) 7 | 8 | def parse(packets): 9 | def consume(n): 10 | nonlocal packets 11 | data, packets = packets[:n], packets[n:] 12 | return data 13 | version = int(consume(3), 2) 14 | type_id = int(consume(3), 2) 15 | if type_id == 4: 16 | value = '' 17 | while True: 18 | literal = consume(5) 19 | value += literal[1:] 20 | if literal[0] == '0': 21 | break 22 | value = int(value, 2) 23 | return value, packets 24 | length_type_id = int(consume(1), 2) 25 | values = [] 26 | if length_type_id == 0: 27 | length = int(consume(15), 2) 28 | subpackets = consume(length) 29 | while subpackets: 30 | value, subpackets = parse(subpackets) 31 | values.append(value) 32 | elif length_type_id == 1: 33 | length = int(consume(11), 2) 34 | for _ in range(length): 35 | value, packets = parse(packets) 36 | values.append(value) 37 | return [ 38 | sum, partial(reduce, mul), min, max, 39 | None, 40 | lambda x: gt(*x), 41 | lambda x: lt(*x), 42 | lambda x: eq(*x) 43 | ][type_id](values), packets 44 | 45 | print(parse(packets)[0]) 46 | -------------------------------------------------------------------------------- /2021/17/17.py: -------------------------------------------------------------------------------- 1 | x1, y1 = 138, -125 2 | x2, y2 = 184, -71 3 | 4 | def simulate(vx, vy): 5 | x, y = 0, 0 6 | maxy = -1 7 | while True: 8 | maxy = max(maxy, y) 9 | if x > x2 or y < y1: 10 | return -1 11 | if x1 <= x and y <= y2: 12 | return maxy 13 | x += vx 14 | y += vy 15 | if vx > 0: 16 | vx -= 1 17 | vy -= 1 18 | 19 | print(max([simulate(vx, vy) for vx in range(1, x2 + 1) for vy in range(1000)])) 20 | -------------------------------------------------------------------------------- /2021/17/17b.py: -------------------------------------------------------------------------------- 1 | x1, y1 = 138, -125 2 | x2, y2 = 184, -71 3 | 4 | def simulate(vx, vy): 5 | x, y = 0, 0 6 | while True: 7 | if x > x2 or y < y1: 8 | return False 9 | if x1 <= x and y <= y2: 10 | return True 11 | x += vx 12 | y += vy 13 | if vx > 0: 14 | vx -= 1 15 | vy -= 1 16 | 17 | print(len([1 for vx in range(1, x2 + 1) for vy in range(-1000, 1000) if simulate(vx, vy)])) 18 | -------------------------------------------------------------------------------- /2021/18/18.py: -------------------------------------------------------------------------------- 1 | from math import ceil 2 | from copy import deepcopy 3 | import json 4 | 5 | with open('in.txt') as f: 6 | lines = f.read().splitlines() 7 | 8 | class ASTNode: 9 | def __init__(self): 10 | self.parent = None 11 | self.parent_direction = None 12 | 13 | def replace(self, other): 14 | if self.parent_direction == 0: 15 | self.parent.left = other 16 | else: 17 | self.parent.right = other 18 | 19 | @classmethod 20 | def from_list(cls, data): 21 | if isinstance(data, int): 22 | return ASTLeaf(data) 23 | res = ASTInternal( 24 | ASTNode.from_list(data[0]), 25 | ASTNode.from_list(data[1]) 26 | ) 27 | res.mark() 28 | return res 29 | 30 | class ASTInternal(ASTNode): 31 | def __init__(self, left, right): 32 | super().__init__() 33 | self.left = left 34 | self.right = right 35 | self.level = None 36 | 37 | def get_lefter(self): 38 | if self.parent is None: 39 | return None 40 | 41 | if self.parent_direction == 0: 42 | return self.parent.get_lefter() 43 | for node in self.parent.left.rtraverse(): 44 | if isinstance(node, ASTLeaf): 45 | return node 46 | 47 | def get_righter(self): 48 | if self.parent is None: 49 | return None 50 | 51 | if self.parent_direction == 1: 52 | return self.parent.get_righter() 53 | for node in self.parent.right.traverse(): 54 | if isinstance(node, ASTLeaf): 55 | return node 56 | 57 | def traverse(self): 58 | yield self 59 | yield from self.left.traverse() 60 | yield from self.right.traverse() 61 | 62 | def rtraverse(self): 63 | yield from self.right.rtraverse() 64 | yield from self.left.rtraverse() 65 | yield self 66 | 67 | def is_pair(self): 68 | return isinstance(self.left, ASTLeaf) and isinstance(self.right, ASTLeaf) 69 | 70 | def mark(self, level=0): 71 | self.left.mark(level + 1) 72 | self.right.mark(level + 1) 73 | self.level = level 74 | self.left.parent = self.right.parent = self 75 | self.left.parent_direction = 0 76 | self.right.parent_direction = 1 77 | 78 | def magnitude(self): 79 | return 3 * self.left.magnitude() + 2 * self.right.magnitude() 80 | 81 | def __add__(self, other): 82 | res = ASTInternal(deepcopy(self), deepcopy(other)) 83 | res.mark() 84 | while True: 85 | try: 86 | res = reduce(res) 87 | except ValueError: 88 | break 89 | res.mark() 90 | return res 91 | 92 | def __repr__(self): 93 | return f'({self.left} {self.right})' 94 | 95 | class ASTLeaf(ASTNode): 96 | def __init__(self, value): 97 | super().__init__() 98 | self.value = value 99 | 100 | def mark(self, level=0): 101 | pass 102 | 103 | def magnitude(self): 104 | return self.value 105 | 106 | def traverse(self): 107 | yield self 108 | 109 | def rtraverse(self): 110 | yield self 111 | 112 | def __repr__(self): 113 | return f'<{str(self.value)}>' 114 | 115 | def reduce(tree): 116 | # explode 117 | for node in tree.traverse(): 118 | if isinstance(node, ASTLeaf): 119 | continue 120 | if node.is_pair() and node.level >= 4: 121 | node.replace(ASTLeaf(0)) 122 | a = node.get_lefter() 123 | if a is not None: 124 | a.value += node.left.value 125 | b = node.get_righter() 126 | if b is not None: 127 | b.value += node.right.value 128 | return tree 129 | 130 | # split 131 | for node in tree.traverse(): 132 | if isinstance(node, ASTLeaf) and node.value >= 10: 133 | node.replace(ASTInternal( 134 | ASTLeaf(node.value // 2), 135 | ASTLeaf(ceil(node.value / 2)) 136 | )) 137 | return tree 138 | 139 | raise ValueError 140 | 141 | nums = [ASTNode.from_list(json.loads(line)) for line in lines] 142 | res = nums[0] 143 | for num in nums[1:]: 144 | res += num 145 | 146 | print(res.magnitude()) 147 | -------------------------------------------------------------------------------- /2021/18/18b.py: -------------------------------------------------------------------------------- 1 | from math import ceil 2 | from copy import deepcopy 3 | from itertools import combinations 4 | import json 5 | 6 | with open('in.txt') as f: 7 | lines = f.read().splitlines() 8 | 9 | class ASTNode: 10 | def __init__(self): 11 | self.parent = None 12 | self.parent_direction = None 13 | 14 | def replace(self, other): 15 | if self.parent_direction == 0: 16 | self.parent.left = other 17 | else: 18 | self.parent.right = other 19 | 20 | @classmethod 21 | def from_list(cls, data): 22 | if isinstance(data, int): 23 | return ASTLeaf(data) 24 | res = ASTInternal( 25 | ASTNode.from_list(data[0]), 26 | ASTNode.from_list(data[1]) 27 | ) 28 | res.mark() 29 | return res 30 | 31 | class ASTInternal(ASTNode): 32 | def __init__(self, left, right): 33 | super().__init__() 34 | self.left = left 35 | self.right = right 36 | self.level = None 37 | 38 | def get_lefter(self): 39 | if self.parent is None: 40 | return None 41 | 42 | if self.parent_direction == 0: 43 | return self.parent.get_lefter() 44 | for node in self.parent.left.rtraverse(): 45 | if isinstance(node, ASTLeaf): 46 | return node 47 | 48 | def get_righter(self): 49 | if self.parent is None: 50 | return None 51 | 52 | if self.parent_direction == 1: 53 | return self.parent.get_righter() 54 | for node in self.parent.right.traverse(): 55 | if isinstance(node, ASTLeaf): 56 | return node 57 | 58 | def traverse(self): 59 | yield self 60 | yield from self.left.traverse() 61 | yield from self.right.traverse() 62 | 63 | def rtraverse(self): 64 | yield from self.right.rtraverse() 65 | yield from self.left.rtraverse() 66 | yield self 67 | 68 | def is_pair(self): 69 | return isinstance(self.left, ASTLeaf) and isinstance(self.right, ASTLeaf) 70 | 71 | def mark(self, level=0): 72 | self.left.mark(level + 1) 73 | self.right.mark(level + 1) 74 | self.level = level 75 | self.left.parent = self.right.parent = self 76 | self.left.parent_direction = 0 77 | self.right.parent_direction = 1 78 | 79 | def magnitude(self): 80 | return 3 * self.left.magnitude() + 2 * self.right.magnitude() 81 | 82 | def __add__(self, other): 83 | res = ASTInternal(deepcopy(self), deepcopy(other)) 84 | res.mark() 85 | while True: 86 | try: 87 | res = reduce(res) 88 | except ValueError: 89 | break 90 | res.mark() 91 | return res 92 | 93 | def __repr__(self): 94 | return f'({self.left} {self.right})' 95 | 96 | class ASTLeaf(ASTNode): 97 | def __init__(self, value): 98 | super().__init__() 99 | self.value = value 100 | 101 | def mark(self, level=0): 102 | pass 103 | 104 | def magnitude(self): 105 | return self.value 106 | 107 | def traverse(self): 108 | yield self 109 | 110 | def rtraverse(self): 111 | yield self 112 | 113 | def __repr__(self): 114 | return f'<{str(self.value)}>' 115 | 116 | def reduce(tree): 117 | # explode 118 | for node in tree.traverse(): 119 | if isinstance(node, ASTLeaf): 120 | continue 121 | if node.is_pair() and node.level >= 4: 122 | node.replace(ASTLeaf(0)) 123 | a = node.get_lefter() 124 | if a is not None: 125 | a.value += node.left.value 126 | b = node.get_righter() 127 | if b is not None: 128 | b.value += node.right.value 129 | return tree 130 | 131 | # split 132 | for node in tree.traverse(): 133 | if isinstance(node, ASTLeaf) and node.value >= 10: 134 | node.replace(ASTInternal( 135 | ASTLeaf(node.value // 2), 136 | ASTLeaf(ceil(node.value / 2)) 137 | )) 138 | return tree 139 | 140 | raise ValueError 141 | 142 | nums = [ASTNode.from_list(json.loads(line)) for line in lines] 143 | m = -1 144 | for a, b in combinations(nums, 2): 145 | m = max(m, (a + b).magnitude()) 146 | 147 | print(m) 148 | -------------------------------------------------------------------------------- /2021/2/2.py: -------------------------------------------------------------------------------- 1 | with open('in.txt') as f: 2 | instructions = f.read().splitlines() 3 | 4 | location = 0 5 | for instruction in instructions: 6 | direction, amount = instruction.split(' ') 7 | amount = int(amount) 8 | location += amount * { 9 | 'forward': 1, 10 | 'down': 1j, 11 | 'up': -1j 12 | }[direction] 13 | 14 | print(int(location.real * location.imag)) 15 | -------------------------------------------------------------------------------- /2021/2/2b.py: -------------------------------------------------------------------------------- 1 | with open('small.txt') as f: 2 | instructions = f.read().splitlines() 3 | 4 | location = 0 5 | aim = 0 6 | for instruction in instructions: 7 | direction, amount = instruction.split(' ') 8 | amount = int(amount) 9 | if direction == 'down': 10 | aim += amount 11 | elif direction == 'up': 12 | aim -= amount 13 | else: 14 | location += amount + aim * amount * 1j 15 | 16 | print(int(location.real * location.imag)) 17 | -------------------------------------------------------------------------------- /2021/20/20.py: -------------------------------------------------------------------------------- 1 | with open('in.txt') as f: 2 | algo, img = f.read().split('\n\n') 3 | 4 | board = set() 5 | for y, line in enumerate(img.split('\n')): 6 | for x, c in enumerate(line): 7 | if c == '#': 8 | board.add((x, y)) 9 | 10 | def enhance(board): 11 | output = set() 12 | xs = set() 13 | ys = set() 14 | 15 | for x, y in board: 16 | xs.add(x) 17 | ys.add(y) 18 | 19 | for x in range(min(xs) - 1, max(xs) + 2): 20 | for y in range(min(ys) - 1, max(ys) + 2): 21 | b = '' 22 | for dy in (-1, 0, 1): 23 | for dx in (-1, 0, 1): 24 | if (x + dx, y + dy) in board: 25 | b += '1' 26 | else: 27 | b += '0' 28 | idx = int(b, 2) 29 | if algo[idx] == '#': 30 | output.add((x, y)) 31 | 32 | return output 33 | 34 | print(len(board)) 35 | -------------------------------------------------------------------------------- /2021/20/20b.py: -------------------------------------------------------------------------------- 1 | with open('in.txt') as f: 2 | algo, img = f.read().split('\n\n') 3 | 4 | algo = [0 if c == '.' else 1 for c in ''.join(algo.split('\n'))] 5 | 6 | board = set() 7 | for y, line in enumerate(img.split('\n')): 8 | for x, c in enumerate(line): 9 | if c == '#': 10 | board.add((x, y)) 11 | 12 | def enhance(board, baseline): 13 | output = set() 14 | xs = set() 15 | ys = set() 16 | 17 | if algo[0] == 1: 18 | new_baseline = not baseline 19 | 20 | for x, y in board: 21 | xs.add(x) 22 | ys.add(y) 23 | 24 | for x in range(min(xs) - 1, max(xs) + 2): 25 | for y in range(min(ys) - 1, max(ys) + 2): 26 | b = '' 27 | for dy in (-1, 0, 1): 28 | for dx in (-1, 0, 1): 29 | if ((x + dx, y + dy) in board) != baseline: 30 | b += '1' 31 | else: 32 | b += '0' 33 | idx = int(b, 2) 34 | if algo[idx] != new_baseline: 35 | output.add((x, y)) 36 | 37 | return output, new_baseline 38 | 39 | baseline = False 40 | for i in range(50): 41 | board, baseline = enhance(board, baseline) 42 | 43 | print(len(board)) 44 | -------------------------------------------------------------------------------- /2021/21/21.py: -------------------------------------------------------------------------------- 1 | p = [5, 8] 2 | s = [0, 0] 3 | mover = False 4 | 5 | cnt = 0 6 | def roll(): 7 | global cnt 8 | cnt += 1 9 | return (cnt - 1) % 100 + 1 10 | 11 | while s[0] < 1000 and s[1] < 1000: 12 | move = roll() + roll() + roll() 13 | p[mover] = (p[mover] - 1 + move) % 10 + 1 14 | s[mover] += p[mover] 15 | mover = not mover 16 | 17 | loser = s[0] >= 1000 18 | print(cnt * s[loser]) 19 | -------------------------------------------------------------------------------- /2021/21/21b.py: -------------------------------------------------------------------------------- 1 | from itertools import product 2 | from functools import cache 3 | 4 | @cache 5 | def cnt(p, s, mover): 6 | if s[0] >= 21: 7 | return [1, 0] 8 | if s[1] >= 21: 9 | return [0, 1] 10 | c = [0, 0] 11 | for d in product((1, 2, 3), repeat=3): 12 | pp = [p[0], p[1]] 13 | pp[mover] = (pp[mover] + sum(d) - 1) % 10 + 1 14 | ss = [s[0], s[1]] 15 | ss[mover] += pp[mover] 16 | 17 | a, b = cnt(tuple(pp), tuple(ss), not mover) 18 | c[0] += a 19 | c[1] += b 20 | return c 21 | 22 | p = (5, 8) 23 | 24 | print(max(cnt(p, (0, 0), 0))) 25 | -------------------------------------------------------------------------------- /2021/22/22.py: -------------------------------------------------------------------------------- 1 | with open('in.txt') as f: 2 | lines = f.read().splitlines() 3 | 4 | on = set() 5 | 6 | for instruction in lines: 7 | toggle, instruction = instruction.split(' ') 8 | toggle = toggle == 'on' 9 | x, y, z = map(lambda x: x.split('=')[1], instruction.split(',')) 10 | minx, maxx = map(int, x.split('..')) 11 | miny, maxy = map(int, y.split('..')) 12 | minz, maxz = map(int, z.split('..')) 13 | if -50 <= minx <= maxx <= 50 and\ 14 | -50 <= miny <= maxy <= 50 and\ 15 | -50 <= minz <= maxz <= 50: 16 | for x in range(minx, maxx + 1): 17 | for y in range(miny, maxy + 1): 18 | for z in range(minz, maxz + 1): 19 | if toggle: 20 | on.add((x, y, z)) 21 | else: 22 | on.discard((x, y, z)) 23 | 24 | print(len(on)) 25 | -------------------------------------------------------------------------------- /2021/22/22b.py: -------------------------------------------------------------------------------- 1 | from copy import copy 2 | from operator import methodcaller 3 | from math import prod 4 | 5 | with open('small.txt') as f: 6 | lines = f.read().splitlines() 7 | 8 | class Segment: 9 | def __init__(self, ends): 10 | self.ends = ends 11 | 12 | def intersect(self, other): 13 | return other.ends[0] in self or\ 14 | self.contains_upper_bound(other.ends[1]) or\ 15 | self.ends[0] in other and other.contains_upper_bound(self.ends[1]) 16 | 17 | def __contains__(self, point): 18 | return self.ends[0] <= point < self.ends[1] 19 | 20 | def contains_upper_bound(self, point): 21 | return self.ends[0] < point <= self.ends[1] 22 | 23 | def size(self): 24 | return self.ends[1] - self.ends[0] 25 | 26 | def __repr__(self): 27 | return str(self.ends) 28 | 29 | class Rect: 30 | def __init__(self, seg): 31 | self.seg = seg 32 | 33 | def within(self, other): 34 | for me, you in zip(self.seg, other.seg): 35 | if me.ends[0] not in you or\ 36 | not you.contains_upper_bound(me.ends[1]): 37 | return False 38 | return True 39 | 40 | def intersect(self, other): 41 | for me, you in zip(self.seg, other.seg): 42 | if not me.intersect(you): 43 | return False 44 | return True 45 | 46 | def size(self): 47 | return prod(seg.size() for seg in self.seg) 48 | 49 | def split(self, dim, point): 50 | if point in self.seg[dim]: 51 | seg1 = copy(self.seg) 52 | seg1[dim] = Segment([seg1[dim].ends[0], point]) 53 | seg2 = copy(self.seg) 54 | seg2[dim] = Segment([point, seg2[dim].ends[1]]) 55 | return set([Rect(seg1), Rect(seg2)]) 56 | return set([self]) 57 | 58 | def __repr__(self): 59 | return str(self.seg) 60 | 61 | def __eq__(self, other): 62 | return str(self) == str(other) 63 | 64 | def __hash__(self): 65 | return hash(self.__repr__()) 66 | 67 | rects = set() 68 | for instruction in lines: 69 | toggle, instruction = instruction.split(' ') 70 | toggle = toggle == 'on' 71 | minmaxs = [x.split('=')[1] for x in instruction.split(',')] 72 | segs = [] 73 | for minmax in minmaxs: 74 | ends = list(map(int, minmax.split('..'))) 75 | ends[1] += 1 76 | segs.append(Segment(ends)) 77 | this = Rect(segs) 78 | 79 | new_rects = set() 80 | for other in rects: 81 | if other.intersect(this): 82 | children = set([other]) 83 | 84 | for dim, seg in enumerate(segs): 85 | for point in seg.ends: 86 | new_children = set() 87 | for child in children: 88 | new_children |= child.split(dim, point) 89 | children = new_children 90 | new_rects |= children 91 | else: 92 | new_rects.add(other) 93 | 94 | rects = set([rect for rect in new_rects if not rect.within(this)]) 95 | if toggle: 96 | rects.add(this) 97 | 98 | print(sum(map(methodcaller('size'), rects))) 99 | -------------------------------------------------------------------------------- /2021/3/3.py: -------------------------------------------------------------------------------- 1 | from collections import Counter 2 | 3 | with open('in.txt') as f: 4 | numbers = f.read().splitlines() 5 | 6 | transposed = zip(*numbers) 7 | 8 | bitcounts = map(Counter, transposed) 9 | gammabits = [x.most_common(1)[0][0] for x in bitcounts] 10 | epsilonbits = ['0' if x == '1' else '1' for x in gammabits] 11 | 12 | gamma = int(''.join(gammabits), 2) 13 | epsilon = int(''.join(epsilonbits), 2) 14 | 15 | print(gamma * epsilon) 16 | -------------------------------------------------------------------------------- /2021/3/3b.py: -------------------------------------------------------------------------------- 1 | from collections import Counter 2 | from itertools import count 3 | from operator import le, gt, itemgetter 4 | 5 | with open('in.txt') as f: 6 | numbers = f.read().splitlines() 7 | 8 | def filter_list(pool, coro): 9 | next(coro) 10 | while len(pool) > 1: 11 | predicate = coro.send(pool) 12 | pool = list(filter(predicate, pool)) 13 | return pool.pop() 14 | 15 | def bitfilter_coro(criterion): 16 | def bitfilter_get_predicate(i, pool): 17 | ith_bits = map(itemgetter(i), pool) 18 | bitcounts = Counter(ith_bits) 19 | desired_bit = str(int(criterion(bitcounts['0'], len(pool) / 2))) 20 | return lambda item: item[i] == desired_bit 21 | pool = (yield) 22 | for i in count(): 23 | pool = (yield bitfilter_get_predicate(i, pool)) 24 | 25 | oxygen = int(filter_list(numbers, bitfilter_coro(le)), 2) 26 | co2 = int(filter_list(numbers, bitfilter_coro(gt)), 2) 27 | 28 | print(oxygen * co2) 29 | -------------------------------------------------------------------------------- /2021/4/4.py: -------------------------------------------------------------------------------- 1 | from sys import exit 2 | from collections import Counter 3 | 4 | with open('in.txt') as f: 5 | file = f.read() 6 | 7 | draws, *boards = file.split('\n\n') 8 | draws = map(int, draws.split(',')) 9 | 10 | boards = [ 11 | [ 12 | [int(cell) for cell in row.split(' ') if cell != ''] 13 | for row in board.split('\n') 14 | ] 15 | for board in boards 16 | ] 17 | 18 | board_dicts = [] 19 | for board in boards: 20 | board_dict = { 21 | cell: (x, y) 22 | for y, row in enumerate(board) 23 | for x, cell in enumerate(row) 24 | } 25 | board_dicts.append((board_dict, Counter(), Counter())) 26 | 27 | for draw in draws: 28 | for idx, (board_dict, rows, cols) in enumerate(board_dicts): 29 | try: 30 | row, col = board_dict[draw] 31 | except KeyError: 32 | continue 33 | rows[row] += 1 34 | cols[col] += 1 35 | del board_dict[draw] 36 | if rows[row] == len(boards[0][0]) or cols[col] == len(boards[0][0]): 37 | print(sum(board_dict) * draw) 38 | exit() 39 | -------------------------------------------------------------------------------- /2021/4/4b.py: -------------------------------------------------------------------------------- 1 | from sys import exit 2 | from collections import Counter 3 | 4 | with open('in.txt') as f: 5 | file = f.read() 6 | 7 | draws, *boards = file.split('\n\n') 8 | draws = map(int, draws.split(',')) 9 | 10 | boards = [ 11 | [ 12 | [int(cell) for cell in row.split(' ') if cell != ''] 13 | for row in board.split('\n') 14 | ] 15 | for board in boards 16 | ] 17 | 18 | board_dicts = [] 19 | for board in boards: 20 | board_dict = { 21 | cell: (x, y) 22 | for y, row in enumerate(board) 23 | for x, cell in enumerate(row) 24 | } 25 | board_dicts.append((board_dict, Counter(), Counter())) 26 | 27 | won = set() 28 | for draw in draws: 29 | for idx, (board_dict, rows, cols) in enumerate(board_dicts): 30 | try: 31 | row, col = board_dict[draw] 32 | except KeyError: 33 | continue 34 | rows[row] += 1 35 | cols[col] += 1 36 | del board_dict[draw] 37 | if rows[row] == len(boards[0][0]) or cols[col] == len(boards[0][0]): 38 | won.add(idx) 39 | if len(won) == len(boards): 40 | print(sum(board_dict) * draw) 41 | exit() 42 | -------------------------------------------------------------------------------- /2021/5/5.py: -------------------------------------------------------------------------------- 1 | from collections import Counter 2 | from itertools import repeat 3 | from parse import parse 4 | 5 | with open('in.txt') as f: 6 | lines = f.read().splitlines() 7 | 8 | points = Counter() 9 | for line in lines: 10 | startx, starty, endx, endy = parse('{:d},{:d} -> {:d},{:d}', line) 11 | if startx == endx: 12 | starty, endy = min(starty, endy), max(starty, endy) 13 | points += Counter(zip(repeat(startx), range(starty, endy + 1))) 14 | elif starty == endy: 15 | startx, endx = min(startx, endx), max(startx, endx) 16 | points += Counter(zip(range(startx, endx + 1), repeat(starty))) 17 | 18 | print(len([elem for elem, cnt in points.items() if cnt > 1])) 19 | -------------------------------------------------------------------------------- /2021/5/5b.py: -------------------------------------------------------------------------------- 1 | from collections import Counter 2 | from parse import parse 3 | from itertools import repeat 4 | from numpy import sign 5 | 6 | with open('in.txt') as f: 7 | lines = f.read().splitlines() 8 | 9 | points = Counter() 10 | for line in lines: 11 | startx, starty, endx, endy = parse('{:d},{:d} -> {:d},{:d}', line) 12 | if startx == endx: 13 | xrange = repeat(startx) 14 | else: 15 | xrange = range(startx, endx + sign(endx - startx), sign(endx - startx)) 16 | if starty == endy: 17 | yrange = repeat(starty) 18 | else: 19 | yrange = range(starty, endy + sign(endy - starty), sign(endy - starty)) 20 | points += Counter(zip(xrange, yrange)) 21 | 22 | print(len([elem for elem, cnt in points.items() if cnt > 1])) 23 | -------------------------------------------------------------------------------- /2021/6/6.py: -------------------------------------------------------------------------------- 1 | with open('in.txt') as f: 2 | fishes = map(int, f.read().split(',')) 3 | 4 | for i in range(80): 5 | new_fishes = [] 6 | for fish in fishes: 7 | if fish == 0: 8 | new_fishes.extend([6, 8]) 9 | else: 10 | new_fishes.append(fish - 1) 11 | fishes = new_fishes 12 | 13 | print(len(fishes)) 14 | -------------------------------------------------------------------------------- /2021/6/6b.py: -------------------------------------------------------------------------------- 1 | from collections import Counter 2 | 3 | with open('in.txt') as f: 4 | fishes = Counter(map(int, f.read().split(','))) 5 | 6 | for i in range(256): 7 | new_fishes = Counter() 8 | for fish, cnt in fishes.items(): 9 | if fish == 0: 10 | new_fishes[6] += cnt 11 | new_fishes[8] += cnt 12 | else: 13 | new_fishes[fish - 1] += cnt 14 | fishes = new_fishes 15 | 16 | print(fishes.total()) 17 | -------------------------------------------------------------------------------- /2021/7/7.py: -------------------------------------------------------------------------------- 1 | with open('in.txt') as f: 2 | crabs = sorted(map(int, f.read().split(','))) 3 | print(sum([abs(crab - crabs[len(crabs) // 2]) for crab in crabs])) 4 | -------------------------------------------------------------------------------- /2021/7/7b.py: -------------------------------------------------------------------------------- 1 | with open('in.txt') as f: 2 | crabs = list(map(int, f.read().split(','))) 3 | 4 | best = float('inf') 5 | for x in range(min(crabs), max(crabs) + 1): 6 | cost = 0 7 | for crab in crabs: 8 | target = abs(crab - x) 9 | cost += target * (target + 1) // 2 10 | best = min(cost, best) 11 | 12 | print(best) 13 | -------------------------------------------------------------------------------- /2021/8/8.py: -------------------------------------------------------------------------------- 1 | with open('in.txt') as f: 2 | lines = f.read().splitlines() 3 | 4 | patterns = [ 5 | 'abcefg', 'cf', 'acdeg', 'acdfg', 'bcdf', 6 | 'abdfg', 'abdefg', 'acf', 'abcdefg', 'abcdfg' 7 | ] 8 | 9 | cnt = 0 10 | for line in lines: 11 | probe, value = line.split(' | ') 12 | for word in value.split(' '): 13 | if len(word) in (2, 4, 3, 7): 14 | cnt += 1 15 | 16 | print(cnt) 17 | -------------------------------------------------------------------------------- /2021/8/8b.py: -------------------------------------------------------------------------------- 1 | from itertools import permutations 2 | 3 | with open('small.txt') as f: 4 | lines = f.read().splitlines() 5 | 6 | alphabet = 'abcdefg' 7 | led_patterns = [ 8 | 'abcefg', 'cf', 'acdeg', 'acdfg', 'bcdf', 9 | 'abdfg', 'abdefg', 'acf', 'abcdefg', 'abcdfg' 10 | ] 11 | inv_patterns = {v: k for k, v in enumerate(led_patterns)} 12 | 13 | def translator(src, dst): 14 | mapping = dict(zip(src, dst)) 15 | def translate(word): 16 | return inv_patterns[''.join(sorted(word.translate(word.maketrans(mapping))))] 17 | return translate 18 | 19 | cnt = 0 20 | for line in lines: 21 | probe, value = line.split(' | ') 22 | for perm in permutations(alphabet): 23 | t = translator(perm, alphabet) 24 | try: 25 | for word in probe.split(' '): 26 | t(word) 27 | except KeyError: 28 | continue 29 | break 30 | decoded = '' 31 | for word in value.split(' '): 32 | decoded += str(t(word)) 33 | cnt += int(decoded) 34 | 35 | print(cnt) 36 | -------------------------------------------------------------------------------- /2021/9/9.py: -------------------------------------------------------------------------------- 1 | with open('in.txt') as f: 2 | lines = f.read().splitlines() 3 | 4 | board = [list(map(int, line)) for line in lines] 5 | 6 | cnt = 0 7 | for y, row in enumerate(board): 8 | for x, cell in enumerate(row): 9 | neighbours = set() 10 | for dx, dy in (0, -1), (0, 1), (-1, 0), (1, 0): 11 | try: 12 | neighbours.add(board[y + dy][x + dx]) 13 | except IndexError: 14 | pass 15 | if all([neighbour > cell for neighbour in neighbours]): 16 | cnt += cell + 1 17 | print(cnt) 18 | -------------------------------------------------------------------------------- /2021/9/9b.py: -------------------------------------------------------------------------------- 1 | with open('in.txt') as f: 2 | lines = f.read().splitlines() 3 | 4 | board = [list(map(int, line)) for line in lines] 5 | 6 | lows = set() 7 | for y, row in enumerate(board): 8 | for x, cell in enumerate(row): 9 | neighbours = set() 10 | for dx, dy in (0, -1), (0, 1), (-1, 0), (1, 0): 11 | try: 12 | neighbours.add(board[y + dy][x + dx]) 13 | except IndexError: 14 | pass 15 | if all([neighbour > cell for neighbour in neighbours]): 16 | lows.add((x, y)) 17 | 18 | visited = set() 19 | basin_sizes = [] 20 | 21 | for x, y in lows: 22 | if (x, y) not in visited: 23 | s = [(x, y)] 24 | visited.add((x, y)) 25 | 26 | cnt = 0 27 | while s: 28 | x, y = s.pop() 29 | cnt += 1 30 | for dx, dy in (0, -1), (0, 1), (-1, 0), (1, 0): 31 | try: 32 | if board[y + dy][x + dx] == 9 or board[y + dy][x + dx] <= board[y][x]: 33 | continue 34 | except IndexError: 35 | continue 36 | if (x + dx, y + dy) not in visited: 37 | s.append((x + dx, y + dy)) 38 | visited.add((x + dx, y + dy)) 39 | 40 | basin_sizes.append(cnt) 41 | 42 | top = sorted(basin_sizes)[-3:] 43 | print(top[0] * top[1] * top[2]) 44 | -------------------------------------------------------------------------------- /2022/1/1.py: -------------------------------------------------------------------------------- 1 | with open('in.txt') as f: 2 | backpacks = [ 3 | [int(food) for food in elf.split('\n')] 4 | for elf in f.read().strip().split('\n\n') 5 | ] 6 | 7 | print(max([sum(foods) for foods in backpacks])) 8 | -------------------------------------------------------------------------------- /2022/1/1b.py: -------------------------------------------------------------------------------- 1 | with open('in.txt') as f: 2 | backpacks = [ 3 | [int(food) for food in elf.split('\n')] 4 | for elf in f.read().strip().split('\n\n') 5 | ] 6 | 7 | print(sum(sorted([sum(foods) for foods in backpacks])[-3:])) 8 | -------------------------------------------------------------------------------- /2022/11/11.py: -------------------------------------------------------------------------------- 1 | from parse import parse 2 | from collections import deque 3 | 4 | with open('input') as f: 5 | instructions = f.read().strip().split('\n\n') 6 | 7 | state = [] 8 | for instruction in instructions: 9 | monkey = parse('Monkey {:d}:' +\ 10 | '{:s}Starting items: {items}' +\ 11 | '{:s}Operation: new = {left:w} {op:S} {right:w}' +\ 12 | '{:s}Test: divisible by {divisor:d}' +\ 13 | '{:s}If true: throw to monkey {toT:d}' +\ 14 | '{:s}If false: throw to monkey {toF:d}', 15 | instruction).named 16 | 17 | monkey['items'] = deque(map(int, monkey['items'].split(', '))) 18 | if monkey['left'] != 'old': 19 | monkey['left'] = int(monkey['left']) 20 | if monkey['right'] != 'old': 21 | monkey['right'] = int(monkey['right']) 22 | monkey['inspections'] = 0 23 | 24 | state.append(monkey) 25 | 26 | for round in range(20): 27 | for monkey in state: 28 | while monkey['items']: 29 | monkey['inspections'] += 1 30 | item = monkey['items'].popleft() 31 | left, op, right = monkey['left'], monkey['op'], monkey['right'] 32 | if left == 'old': 33 | left = item 34 | if right == 'old': 35 | right = item 36 | new = {'+': left + right, 37 | '*': left * right}[op] // 3 38 | if new % monkey['divisor'] == 0: 39 | state[monkey['toT']]['items'].append(new) 40 | else: 41 | state[monkey['toF']]['items'].append(new) 42 | 43 | performers = sorted(state, key=lambda x: x['inspections'], reverse=True) 44 | print(performers[0]['inspections'] * performers[1]['inspections']) 45 | -------------------------------------------------------------------------------- /2022/11/11b.py: -------------------------------------------------------------------------------- 1 | from parse import parse 2 | from collections import deque 3 | from operator import add, mul 4 | 5 | with open('input') as f: 6 | instructions = f.read().strip().split('\n\n') 7 | 8 | divisors = [] 9 | state = [] 10 | for instruction in instructions: 11 | monkey = parse('Monkey {:d}:' +\ 12 | '{:s}Starting items: {items}' +\ 13 | '{:s}Operation: new = {left:w} {op:S} {right:w}' +\ 14 | '{:s}Test: divisible by {divisor:d}' +\ 15 | '{:s}If true: throw to monkey {toT:d}' +\ 16 | '{:s}If false: throw to monkey {toF:d}', 17 | instruction).named 18 | 19 | monkey['items'] = [int(item) for item in monkey['items'].split(', ')] 20 | if monkey['left'] != 'old': 21 | monkey['left'] = int(monkey['left']) 22 | if monkey['right'] != 'old': 23 | monkey['right'] = int(monkey['right']) 24 | 25 | state.append(monkey | { 26 | 'ditems': deque([]), 27 | 'inspections': 0 28 | }) 29 | 30 | divisors.append(monkey['divisor']) 31 | 32 | for monkey in state: 33 | for item in monkey['items']: 34 | d = {} 35 | for divisor in divisors: 36 | d[divisor] = item % divisor 37 | monkey['ditems'].append(d) 38 | 39 | for round in range(10000): 40 | for j in range(len(state)): 41 | monkey = state[j] 42 | while monkey['ditems']: 43 | monkey['inspections'] += 1 44 | item = monkey['ditems'].popleft() 45 | op = { '+': add, '*': mul }[monkey['op']] 46 | for divisor in divisors: 47 | left, right = monkey['left'], monkey['right'] 48 | if left == 'old': 49 | left = item[divisor] 50 | if right == 'old': 51 | right = item[divisor] 52 | item[divisor] = op(left, right) % divisor 53 | 54 | if item[monkey['divisor']] == 0: 55 | state[monkey['toT']]['ditems'].append(item) 56 | else: 57 | state[monkey['toF']]['ditems'].append(item) 58 | 59 | performers = sorted(state, key=lambda x: x['inspections'], reverse=True) 60 | print(performers[0]['inspections'] * performers[1]['inspections']) 61 | -------------------------------------------------------------------------------- /2022/12/12.py: -------------------------------------------------------------------------------- 1 | from collections import deque 2 | 3 | with open('input') as f: 4 | lines = f.read().strip().splitlines() 5 | 6 | G = [] 7 | start, end = None, None 8 | for y, line in enumerate(lines): 9 | G.append([]) 10 | for x, c in enumerate(line): 11 | if c == 'S': 12 | start = x, y 13 | c = 'a' 14 | elif c == 'E': 15 | end = x, y 16 | c = 'z' 17 | G[y].append(ord(c)) 18 | 19 | def BFS(G, pos): 20 | q = deque([(pos, 0)]) 21 | seen = set([pos]) 22 | while q: 23 | (x, y), d = q.popleft() 24 | if (x, y) == end: 25 | return d 26 | for dx, dy in (0, 1), (0, -1), (1, 0), (-1, 0): 27 | nx, ny = x + dx, y + dy 28 | if (nx, ny) in seen: 29 | continue 30 | if 0 <= nx < len(G[0]) and 0 <= ny < len(G): 31 | if G[ny][nx] > G[y][x] + 1: 32 | continue 33 | seen.add((nx, ny)) 34 | q.append(((nx, ny), d + 1)) 35 | 36 | print(BFS(G, start)) 37 | -------------------------------------------------------------------------------- /2022/12/12b.py: -------------------------------------------------------------------------------- 1 | from collections import deque 2 | 3 | with open('input') as f: 4 | lines = f.read().strip().splitlines() 5 | 6 | G = [] 7 | start = None 8 | for y, line in enumerate(lines): 9 | G.append([]) 10 | for x, c in enumerate(line): 11 | if c == 'S': 12 | c = 'a' 13 | elif c == 'E': 14 | start = x, y 15 | c = 'z' 16 | G[y].append(ord(c) - ord('a')) 17 | 18 | def BFS(G, pos): 19 | q = deque([(pos, 0)]) 20 | seen = set([pos]) 21 | while q: 22 | (x, y), d = q.popleft() 23 | if G[y][x] == 0: 24 | return d 25 | for dx, dy in ((0, 1), (0, -1), (1, 0), (-1, 0)): 26 | nx, ny = x + dx, y + dy 27 | if (nx, ny) in seen: 28 | continue 29 | if 0 <= nx < len(G[0]) and 0 <= ny < len(G): 30 | if G[y][x] > G[ny][nx] + 1: 31 | continue 32 | seen.add((nx, ny)) 33 | q.append(((nx, ny), d + 1)) 34 | 35 | print(BFS(G, start)) 36 | -------------------------------------------------------------------------------- /2022/13/13.py: -------------------------------------------------------------------------------- 1 | from json import loads 2 | 3 | with open('input') as f: 4 | lines = f.read().strip().split('\n\n') 5 | 6 | def compare(a, b): 7 | if isinstance(a, int): 8 | if isinstance(b, int): 9 | return a - b 10 | return compare([a], b) 11 | if isinstance(b, int): 12 | return compare(a, [b]) 13 | for p, q in zip(a, b): 14 | c = compare(p, q) 15 | if c != 0: 16 | return c 17 | return len(a) - len(b) 18 | 19 | c = 0 20 | for i, line in enumerate(lines): 21 | a, b = line.strip().splitlines() 22 | a = loads(a) 23 | b = loads(b) 24 | if compare(a, b) < 0: 25 | c += i + 1 26 | print(c) 27 | -------------------------------------------------------------------------------- /2022/13/13b.py: -------------------------------------------------------------------------------- 1 | from functools import cmp_to_key 2 | from json import loads 3 | 4 | with open('input') as f: 5 | lines = f.read().strip().split('\n\n') 6 | 7 | def compare(a, b): 8 | if isinstance(a, int): 9 | if isinstance(b, int): 10 | return a - b 11 | return compare([a], b) 12 | if isinstance(b, int): 13 | return compare(a, [b]) 14 | for p, q in zip(a, b): 15 | c = compare(p, q) 16 | if c != 0: 17 | return c 18 | return len(a) - len(b) 19 | 20 | l = [[[2]], [[6]]] 21 | 22 | for i, line in enumerate(lines): 23 | a, b = line.strip().splitlines() 24 | l.append(loads(a)) 25 | l.append(loads(b)) 26 | 27 | l.sort(key=cmp_to_key(compare)) 28 | r = l.index([[2]]) + 1 29 | s = l.index([[6]]) + 1 30 | print(r * s) 31 | -------------------------------------------------------------------------------- /2022/2/2.py: -------------------------------------------------------------------------------- 1 | with open('in.txt') as f: 2 | games = [line.split(' ') for line in f.read().splitlines()] 3 | 4 | score = 0 5 | for other, me in games: 6 | other = ord(other) - ord('A') 7 | me = ord(me) - ord('X') 8 | score += 3 * ((me - other + 1) % 3) + me + 1 9 | print(score) 10 | -------------------------------------------------------------------------------- /2022/2/2b.py: -------------------------------------------------------------------------------- 1 | with open('in.txt') as f: 2 | games = [line.split(' ') for line in f.read().splitlines()] 3 | 4 | score = 0 5 | for other, result in games: 6 | other = ord(other) - ord('A') 7 | result = ord(result) - ord('X') 8 | score += result * 3 + (result + other - 1) % 3 + 1 9 | print(score) 10 | -------------------------------------------------------------------------------- /2022/3/3.py: -------------------------------------------------------------------------------- 1 | with open('in.txt') as f: 2 | lines = f.read().strip().split('\n') 3 | 4 | total = 0 5 | for line in lines: 6 | left, right = line[:len(line)//2], line[len(line)//2:] 7 | common = ord((set(left) & set(right)).pop()) 8 | if ord('a') <= common <= ord('z'): 9 | score = common - ord('a') + 1 10 | else: 11 | score = common - ord('A') + 27 12 | total += score 13 | print(total) 14 | -------------------------------------------------------------------------------- /2022/3/3b.py: -------------------------------------------------------------------------------- 1 | with open('in.txt') as f: 2 | lines = [set(line) for line in f.read().strip().split('\n')] 3 | 4 | total = 0 5 | for knapsacks in zip(lines[::3], lines[1::3], lines[2::3]): 6 | common = ord((set.intersection(*knapsacks)).pop()) 7 | if ord('a') <= common <= ord('z'): 8 | score = common - ord('a') + 1 9 | else: 10 | score = common - ord('A') + 27 11 | total += score 12 | print(total) 13 | -------------------------------------------------------------------------------- /2022/4/4.py: -------------------------------------------------------------------------------- 1 | with open('in.txt') as f: 2 | lines = f.read().strip().splitlines() 3 | 4 | c = 0 5 | for line in lines: 6 | a, b = line.split(',') 7 | la, ra = a.split('-') 8 | lb, rb = b.split('-') 9 | la, lb, ra, rb = int(la), int(lb), int(ra), int(rb) 10 | if la <= lb and rb <= ra\ 11 | or lb <= la and ra <= rb: 12 | c += 1 13 | print(c) 14 | -------------------------------------------------------------------------------- /2022/4/4b.py: -------------------------------------------------------------------------------- 1 | with open('in.txt') as f: 2 | lines = f.read().strip().splitlines() 3 | 4 | c = 0 5 | for line in lines: 6 | a, b = line.split(',') 7 | la, ra = a.split('-') 8 | lb, rb = b.split('-') 9 | la, lb, ra, rb = int(la), int(lb), int(ra), int(rb) 10 | if la <= lb and lb <= ra\ 11 | or lb <= la and la <= rb: 12 | c += 1 13 | print(c) 14 | -------------------------------------------------------------------------------- /2022/5/5.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | 3 | with open('in.txt') as f: 4 | arrangement, moves = f.read().split('\n\n') 5 | 6 | piles = defaultdict(list) 7 | for boxes in arrangement.split('\n')[::-1][1:]: 8 | for i, c in enumerate(boxes[1::4]): 9 | if c != ' ': 10 | piles[i + 1].append(c) 11 | 12 | for move in moves.strip().split('\n'): 13 | _, cnt, _, src, _, dst = move.split() 14 | cnt, src, dst = int(cnt), int(src), int(dst) 15 | for _ in range(cnt): 16 | piles[dst].append(piles[src].pop()) 17 | 18 | ans = '' 19 | for pile in piles.values(): 20 | ans += pile[-1] 21 | print(ans) 22 | -------------------------------------------------------------------------------- /2022/5/5b.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | 3 | with open('in.txt') as f: 4 | arrangement, moves = f.read().split('\n\n') 5 | 6 | piles = defaultdict(list) 7 | for boxes in arrangement.split('\n')[::-1][1:]: 8 | for i, c in enumerate(boxes[1::4]): 9 | if c != ' ': 10 | piles[i + 1].append(c) 11 | 12 | for move in moves.strip().split('\n'): 13 | _, cnt, _, src, _, dst = move.split() 14 | cnt, src, dst = int(cnt), int(src), int(dst) 15 | piles[dst].extend(piles[src][-cnt:]) 16 | piles[src] = piles[src][:-cnt] 17 | 18 | ans = '' 19 | for pile in piles.values(): 20 | ans += pile[-1] 21 | print(ans) 22 | -------------------------------------------------------------------------------- /2022/6/6.py: -------------------------------------------------------------------------------- 1 | with open('in.txt') as f: 2 | packet = f.read().strip() 3 | 4 | for i in range(len(packet)): 5 | marker = packet[i:i+4] 6 | if len(set(marker)) == 4: 7 | print(i + 4) 8 | break 9 | -------------------------------------------------------------------------------- /2022/6/6b.py: -------------------------------------------------------------------------------- 1 | with open('in.txt') as f: 2 | packet = f.read().strip() 3 | 4 | for i in range(len(packet)): 5 | marker = packet[i:i+14] 6 | if len(set(marker)) == 14: 7 | print(i + 14) 8 | break 9 | -------------------------------------------------------------------------------- /2022/7/7.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | 3 | with open('in.txt') as f: 4 | executions = f.read().strip().split('$ ') 5 | 6 | tree = {} 7 | pwd = [] 8 | 9 | for execution in executions[1:]: 10 | command, *output = execution.splitlines() 11 | program, *args = command.split(' ') 12 | if program == 'ls': 13 | for line in output: 14 | size, name = line.split(' ') 15 | if size != 'dir': 16 | tree[tuple(pwd) + (name,)] = int(size) 17 | else: # program == 'cd' 18 | if args[0] == '..': 19 | pwd.pop() 20 | else: 21 | pwd.append(args[0]) 22 | 23 | sizes = defaultdict(int) 24 | for path, size in tree.items(): 25 | pwd = [] 26 | for dir in path: 27 | sizes[tuple(pwd)] += size 28 | pwd.append(dir) 29 | 30 | print(sum([size for size in sizes.values() if size <= 100000])) 31 | -------------------------------------------------------------------------------- /2022/7/7b.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | 3 | with open('in.txt') as f: 4 | executions = f.read().strip().split('$ ') 5 | 6 | tree = {} 7 | pwd = [] 8 | 9 | for execution in executions[1:]: 10 | command, *output = execution.splitlines() 11 | program, *args = command.split(' ') 12 | if program == 'ls': 13 | for line in output: 14 | size, name = line.split(' ') 15 | if size != 'dir': 16 | tree[tuple(pwd) + (name,)] = int(size) 17 | else: # program == 'cd' 18 | if args[0] == '..': 19 | pwd.pop() 20 | else: 21 | pwd.append(args[0]) 22 | 23 | sizes = defaultdict(int) 24 | for path, size in tree.items(): 25 | pwd = [] 26 | for dir in path: 27 | sizes[tuple(pwd)] += size 28 | pwd.append(dir) 29 | 30 | total = 70000000 31 | needed = 30000000 32 | taken = sizes[()] 33 | 34 | for size in sorted(sizes.values()): 35 | if total - (taken - size) >= needed: 36 | print(size) 37 | break 38 | -------------------------------------------------------------------------------- /2022/8/8.py: -------------------------------------------------------------------------------- 1 | # Dionysis & Joachim 2 | 3 | with open('in.txt') as f: 4 | A = list(f.read().strip().splitlines()) 5 | 6 | A = [[int(c) for c in line.strip()] for line in A] 7 | T = list(zip(*A)) 8 | 9 | cnt = 0 10 | for y, row in enumerate(A): 11 | for x, c in enumerate(row): 12 | views = [A[y][:x][::-1], A[y][x + 1:], T[x][:y][::-1], T[x][y + 1:]] 13 | cnt += any(all(tree < c for tree in view) for view in views) 14 | print(cnt) -------------------------------------------------------------------------------- /2022/8/8b.py: -------------------------------------------------------------------------------- 1 | # Dionysis & Joachim 2 | 3 | with open('in.txt') as f: 4 | A = list(f.read().strip().splitlines()) 5 | 6 | A = [[int(c) for c in line.strip()] for line in A] 7 | T = list(zip(*A)) 8 | 9 | best = 0 10 | for y, row in enumerate(A): 11 | for x, tree in enumerate(row): 12 | views = [A[y][:x][::-1], A[y][x + 1:], T[x][:y][::-1], T[x][y + 1:]] 13 | score = 1 14 | for view in views: 15 | try: 16 | score *= list(v >= tree for v in view).index(True) + 1 17 | except ValueError: 18 | score *= len(view) 19 | best = max(score, best) 20 | print(best) -------------------------------------------------------------------------------- /2022/9/9.py: -------------------------------------------------------------------------------- 1 | with open('in.txt') as f: 2 | lines = f.read().strip().splitlines() 3 | 4 | s = set([0]) 5 | h, t = 0, 0 6 | 7 | for line in lines: 8 | dir, cnt = line.split() 9 | cnt = int(cnt) 10 | for _ in range(cnt): 11 | prev = h 12 | h += { 13 | 'U': -1j, 14 | 'D': 1j, 15 | 'R': 1, 16 | 'L': -1 17 | }[dir] 18 | if abs(t - h) >= 2: 19 | t = prev 20 | s.add(t) 21 | 22 | print(len(s)) 23 | -------------------------------------------------------------------------------- /2022/9/9b.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | with open('in.txt') as f: 4 | lines = f.read().strip().splitlines() 5 | 6 | s = set([0]) 7 | h = 10 * [0] 8 | 9 | for line in lines: 10 | dir, cnt = line.split() 11 | cnt = int(cnt) 12 | for _ in range(cnt): 13 | h[0] += { 14 | 'U': -1j, 15 | 'D': 1j, 16 | 'R': 1, 17 | 'L': -1 18 | }[dir] 19 | for i in range(9): 20 | if abs(h[i + 1] - h[i]) < 2: 21 | break 22 | delta = h[i] - h[i + 1] 23 | h[i + 1] += np.sign(delta.real) + 1j * np.sign(delta.imag) 24 | s.add(h[-1]) 25 | 26 | print(len(s)) 27 | -------------------------------------------------------------------------------- /2024/.gitignore: -------------------------------------------------------------------------------- 1 | *.txt 2 | -------------------------------------------------------------------------------- /2024/01/01a.py: -------------------------------------------------------------------------------- 1 | with open('in.txt') as f: 2 | lines = f.readlines() 3 | 4 | lines = [(int(num) for num in pair.split()) for pair in lines] 5 | lists = map(sorted, zip(*lines)) 6 | diffs = [abs(b - a) for a, b in zip(*lists)] 7 | 8 | print(sum(diffs)) 9 | -------------------------------------------------------------------------------- /2024/01/01b.py: -------------------------------------------------------------------------------- 1 | from collections import Counter 2 | 3 | with open('in.txt') as f: 4 | lines = f.readlines() 5 | 6 | lines = [(int(num) for num in pair.split()) for pair in lines] 7 | lists = list(zip(*lines)) 8 | first = lists[0] 9 | second = Counter(lists[1]) 10 | 11 | print(sum(num * second[num] for num in first)) 12 | -------------------------------------------------------------------------------- /2024/02/02a.py: -------------------------------------------------------------------------------- 1 | with open('in.txt') as f: 2 | lines = f.readlines() 3 | 4 | def safe(report): 5 | diffs = [b - a for a, b in zip(report, report[1:])] 6 | if any(diff > 0 for diff in diffs) and any(diff < 0 for diff in diffs): 7 | return False 8 | diffs = [abs(num) for num in diffs] 9 | return all(diff >= 1 and diff <= 3 for diff in diffs) 10 | 11 | cnt = 0 12 | for report in lines: 13 | nums = [int(num) for num in report.split()] 14 | cnt += safe(nums) 15 | 16 | print(cnt) 17 | -------------------------------------------------------------------------------- /2024/02/02b.py: -------------------------------------------------------------------------------- 1 | with open('in.txt') as f: 2 | lines = f.readlines() 3 | 4 | def safe(report): 5 | diffs = [b - a for a, b in zip(report, report[1:])] 6 | if any(diff > 0 for diff in diffs) and any(diff < 0 for diff in diffs): 7 | return False 8 | diffs = [abs(num) for num in diffs] 9 | return all(diff >= 1 and diff <= 3 for diff in diffs) 10 | 11 | cnt = 0 12 | for report in lines: 13 | nums = [int(num) for num in report.split()] 14 | for i in range(len(nums)): 15 | fixed = nums[:i] + nums[i+1:] 16 | if safe(fixed): 17 | cnt += 1 18 | break 19 | 20 | print(cnt) 21 | -------------------------------------------------------------------------------- /2024/03/03a.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | with open('in.txt') as f: 4 | memory = f.read() 5 | 6 | matches = re.findall(r'mul\((\d{1,3}),(\d{1,3})\)', memory) 7 | 8 | res = sum([int(a) * int(b) for a, b in matches]) 9 | print(res) -------------------------------------------------------------------------------- /2024/03/03b.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | with open('in.txt') as f: 4 | memory = f.read() 5 | 6 | matches = re.findall(r'(mul)\((\d{1,3}),(\d{1,3})\)|(do)\(\)|(don\'t)\(\)', memory) 7 | 8 | sum = 0 9 | enabled = True 10 | for match in matches: 11 | if match[3]: # do 12 | enabled = True 13 | elif match[4]: # don't 14 | enabled = False 15 | elif enabled: 16 | sum += int(match[1]) * int(match[2]) 17 | 18 | print(sum) 19 | -------------------------------------------------------------------------------- /2024/04/04a.py: -------------------------------------------------------------------------------- 1 | from collections import Counter 2 | 3 | with open('in.txt') as f: 4 | board = [line.strip() for line in f.readlines()] 5 | 6 | def get_candidates(board, dx, dy): 7 | candidates = [] 8 | for i, line in enumerate(board): 9 | for j, _ in enumerate(line): 10 | candidate = '' 11 | for k in range(4): 12 | if 0 <= i+k*dy < len(board) and 0 <= j+k*dx < len(line): 13 | candidate += board[i+k*dy][j+k*dx] 14 | else: 15 | break 16 | else: 17 | candidates.append(candidate) 18 | 19 | return candidates 20 | 21 | candidates = [] 22 | for dx in (-1, 0, 1): 23 | for dy in (-1, 0, 1): 24 | if dx != 0 or dy != 0: 25 | candidates += get_candidates(board, dx, dy) 26 | 27 | print(Counter(candidates)['XMAS']) -------------------------------------------------------------------------------- /2024/04/04b.py: -------------------------------------------------------------------------------- 1 | from collections import Counter 2 | 3 | with open('in.txt') as f: 4 | board = [line.strip() for line in f.readlines()] 5 | 6 | def get_candidates(board, dx, dy): 7 | candidates = [] 8 | for i, line in enumerate(board): 9 | for j, _ in enumerate(line): 10 | candidate = '' 11 | for k in range(3): 12 | if 0 <= i+k*dy < len(board) and 0 <= j+k*dx < len(line): 13 | candidate += board[i+k*dy][j+k*dx] 14 | else: 15 | break 16 | else: 17 | if candidate == 'MAS': 18 | candidates.append((i + dy, j + dx)) 19 | 20 | return candidates 21 | 22 | candidates = [] 23 | for dx, dy in ((-1, -1), (-1, 1), (1, -1), (1, 1)): 24 | candidates += get_candidates(board, dx, dy) 25 | 26 | print(len([c for c in Counter(candidates).most_common() if c[1] == 2])) -------------------------------------------------------------------------------- /2024/05/05a.py: -------------------------------------------------------------------------------- 1 | from itertools import combinations 2 | 3 | with open('in.txt') as f: 4 | data = f.read() 5 | 6 | rules, updates = data.split('\n\n') 7 | rules = rules.split('\n') 8 | rules = set([tuple(int(v) for v in rule.split('|')) for rule in rules]) 9 | updates = updates.split('\n') 10 | updates = [[int(v) for v in update.split(',')] for update in updates] 11 | 12 | def topo(update): 13 | for u, v in combinations(update, 2): 14 | if (v, u) in rules: 15 | return False 16 | return True 17 | 18 | s = 0 19 | for update in updates: 20 | if topo(update): 21 | s += update[len(update) // 2] 22 | 23 | print(s) 24 | -------------------------------------------------------------------------------- /2024/05/05b.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | from heapq import heappush, heappop 3 | 4 | with open('in.txt') as f: 5 | data = f.read() 6 | 7 | rules, updates = data.split('\n\n') 8 | rules = rules.split('\n') 9 | rules = set([tuple(int(v) for v in rule.split('|')) for rule in rules]) 10 | updates = updates.split('\n') 11 | updates = [[int(v) for v in update.split(',')] for update in updates] 12 | 13 | def subgraph(G, V): 14 | G_sub = {} 15 | for u in G: 16 | if u in V: 17 | G_sub[u] = set() 18 | for v in G[u]: 19 | if v in V: 20 | G_sub[u].add(v) 21 | return G_sub 22 | 23 | def nodes(G): 24 | V = set() 25 | for u in G: 26 | V.add(u) 27 | for v in G[u]: 28 | V.add(v) 29 | return V 30 | 31 | def toposort(G): 32 | visited = set() 33 | 34 | V = nodes(G) 35 | degree = defaultdict(int) 36 | for u in G: 37 | for v in G[u]: 38 | degree[v] += 1 39 | heap = [] 40 | for u in V: 41 | heappush(heap, (degree[u], u)) 42 | 43 | while heap: 44 | _, u = heappop(heap) 45 | 46 | if u not in visited: 47 | yield u 48 | visited.add(u) 49 | 50 | for v in G[u]: 51 | degree[v] -= 1 52 | heappush(heap, (degree[v], v)) 53 | 54 | G = {} 55 | 56 | for u, v in rules: 57 | G[u] = set() 58 | G[v] = set() 59 | 60 | for u, v in rules: 61 | G[u].add(v) 62 | 63 | s = 0 64 | for V in updates: 65 | G_sub = subgraph(G, V) 66 | toposorted = list(toposort(G_sub)) 67 | if V != toposorted: 68 | s += toposorted[len(toposorted) // 2] 69 | 70 | print(s) -------------------------------------------------------------------------------- /2024/06/06a.py: -------------------------------------------------------------------------------- 1 | with open('in.txt') as f: 2 | lines = [line.strip() for line in f.readlines()] 3 | 4 | board = {} 5 | for y, line in enumerate(lines): 6 | for x, c in enumerate(line): 7 | idx = x - y * 1j 8 | if c == '^': 9 | pos = idx 10 | dir = 1j 11 | board[idx] = c 12 | 13 | visited = set() 14 | while True: 15 | visited.add(pos) 16 | new_pos = pos + dir 17 | if new_pos not in board: 18 | break 19 | if board[new_pos] == '#': 20 | dir *= -1j 21 | continue 22 | pos = new_pos 23 | 24 | print(len(visited)) 25 | -------------------------------------------------------------------------------- /2024/06/06b.py: -------------------------------------------------------------------------------- 1 | with open('in.txt') as f: 2 | lines = [line.strip() for line in f.readlines()] 3 | 4 | board = {} 5 | for y, line in enumerate(lines): 6 | for x, c in enumerate(line): 7 | idx = x - y * 1j 8 | if c == '^': 9 | pos = idx 10 | dir = 1j 11 | board[idx] = c 12 | 13 | def loops(board, pos, dir): 14 | visited = set() 15 | while True: 16 | if (pos, dir) in visited: 17 | return True 18 | visited.add((pos, dir)) 19 | new_pos = pos + dir 20 | if new_pos not in board: 21 | break 22 | if board[new_pos] == '#': 23 | dir *= -1j 24 | continue 25 | pos = new_pos 26 | return False 27 | 28 | cnt = 0 29 | for y in range(len(lines)): 30 | for x in range(len(lines[0])): 31 | idx = x - y * 1j 32 | if board[idx] == '.': 33 | board[idx] = '#' 34 | cnt += loops(board, pos, dir) 35 | board[idx] = '.' 36 | 37 | print(cnt) -------------------------------------------------------------------------------- /2024/07/07a.py: -------------------------------------------------------------------------------- 1 | with open('in.txt') as f: 2 | lines = [line.strip() for line in f.readlines()] 3 | 4 | sum = 0 5 | for line in lines: 6 | test, eqn = line.split(': ') 7 | test = int(test) 8 | nums = [int(num) for num in eqn.split(' ')] 9 | 10 | for b in range(1 << (len(nums) - 1)): 11 | total = nums[0] 12 | for i in range(len(nums) - 1): 13 | if b & (1 << i): 14 | total += nums[i + 1] 15 | else: 16 | total *= nums[i + 1] 17 | 18 | if total == test: 19 | sum += test 20 | break 21 | 22 | print(sum) -------------------------------------------------------------------------------- /2024/07/07b.py: -------------------------------------------------------------------------------- 1 | with open('in.txt') as f: 2 | lines = [line.strip() for line in f.readlines()] 3 | 4 | sum = 0 5 | for line in lines: 6 | test, eqn = line.split(': ') 7 | test = int(test) 8 | nums = [int(num) for num in eqn.split(' ')] 9 | 10 | for b in range(3 ** (len(nums) - 1)): 11 | total = nums[0] 12 | for i in range(len(nums) - 1): 13 | if b % 3 == 0: 14 | total += nums[i + 1] 15 | elif b % 3 == 1: 16 | total *= nums[i + 1] 17 | elif b % 3 == 2: 18 | total = int(str(total) + str(nums[i + 1])) 19 | b //= 3 20 | 21 | if total == test: 22 | sum += test 23 | break 24 | 25 | print(sum) -------------------------------------------------------------------------------- /2024/08/08a.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | from itertools import permutations 3 | 4 | with open('in.txt') as f: 5 | board = [line.strip() for line in f.readlines()] 6 | 7 | loc = defaultdict(list) 8 | for y, row in enumerate(board): 9 | for x, c in enumerate(row): 10 | if c != '.': 11 | loc[c].append(x + y * 1j) 12 | 13 | visited = set() 14 | for c, pts in loc.items(): 15 | for a, b in permutations(pts, 2): 16 | q = 2*b - a 17 | if 0 <= q.real < len(board[0]) and 0 <= q.imag < len(board): 18 | visited.add(q) 19 | 20 | print(len(visited)) -------------------------------------------------------------------------------- /2024/08/08b.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | from itertools import combinations 3 | from math import gcd 4 | 5 | with open('in.txt') as f: 6 | board = [line.strip() for line in f.readlines()] 7 | 8 | loc = defaultdict(list) 9 | for y, row in enumerate(board): 10 | for x, c in enumerate(row): 11 | if c != '.': 12 | loc[c].append(x + y * 1j) 13 | 14 | visited = set() 15 | for c, pts in loc.items(): 16 | for a, b in combinations(pts, 2): 17 | q = b - a 18 | g = gcd(int(q.real), int(q.imag)) 19 | q = int(q.real) // g + int(q.imag) // g * 1j 20 | if q.real < 0: 21 | q = -q 22 | 23 | for x in range(len(board[0])): 24 | for y in range(len(board)): 25 | if (x - a.real) % q.real == 0 and (y - a.imag) % q.imag == 0: 26 | if (x - a.real) // q.real == (y - a.imag) // q.imag: 27 | visited.add(x + y * 1j) 28 | 29 | print(len(visited)) -------------------------------------------------------------------------------- /2024/09/09a.py: -------------------------------------------------------------------------------- 1 | with open('in.txt') as f: 2 | s = f.read().strip() 3 | 4 | disk = [] 5 | id = 0 6 | for c in s: 7 | for _ in range(int(c)): 8 | if id % 2 == 0: 9 | disk.append(id // 2) 10 | else: 11 | disk.append(None) 12 | id += 1 13 | 14 | i = int(s[0]) 15 | j = len(disk) - 1 16 | 17 | while i < j: 18 | while disk[i] is not None: 19 | i += 1 20 | while disk[j] is None: 21 | j -= 1 22 | if i >= j: 23 | break 24 | disk[i], disk[j] = disk[j], disk[i] 25 | i += 1 26 | j -= 1 27 | 28 | disk = disk[:i+1] 29 | 30 | print(sum([i * id for i, id in enumerate(disk) if id is not None])) 31 | -------------------------------------------------------------------------------- /2024/09/09b.py: -------------------------------------------------------------------------------- 1 | with open('in.txt') as f: 2 | s = f.read().strip() 3 | 4 | files = [] 5 | space = [] 6 | 7 | id = 0 8 | pos = 0 9 | for c in s: 10 | if id % 2 == 0: 11 | files.append([id // 2, pos, int(c)]) 12 | else: 13 | space.append([pos, int(c)]) 14 | pos += int(c) 15 | id += 1 16 | 17 | files = files[::-1] 18 | for file in files: 19 | id, file_pos, file_size = file 20 | for i, (space_pos, space_size) in enumerate(space): 21 | if file_size <= space_size and space_pos < file_pos: 22 | space[i] = [space_pos + file_size, space_size - file_size] 23 | file[1] = space_pos 24 | break 25 | 26 | check = 0 27 | for id, file_pos, file_size in files: 28 | for i in range(file_size): 29 | check += (file_pos + i) * id 30 | print(check) 31 | -------------------------------------------------------------------------------- /2024/10/10a.py: -------------------------------------------------------------------------------- 1 | with open('in.txt') as f: 2 | board = [list(map(int, line.strip())) for line in f.readlines()] 3 | 4 | def score(board, x, y): 5 | ret = 0 6 | 7 | visited = set((x, y)) 8 | frontier = [(x, y)] 9 | 10 | while frontier: 11 | x, y = frontier.pop() 12 | 13 | if (x, y) in visited: 14 | continue 15 | 16 | visited.add((x, y)) 17 | 18 | if board[y][x] == 9: 19 | ret += 1 20 | continue 21 | 22 | for dx, dy in (0, -1), (0, 1), (-1, 0), (1, 0): 23 | if 0 <= x + dx < len(board[0]) and 0 <= y + dy < len(board): 24 | if board[y + dy][x + dx] == board[y][x] + 1: 25 | frontier.append((x + dx, y + dy)) 26 | 27 | return ret 28 | 29 | tot = 0 30 | for y in range(len(board)): 31 | for x in range(len(board[0])): 32 | if board[y][x] == 0: 33 | tot += score(board, x, y) 34 | print(tot) -------------------------------------------------------------------------------- /2024/10/10b.py: -------------------------------------------------------------------------------- 1 | from collections import deque, defaultdict 2 | 3 | with open('in.txt') as f: 4 | board = [list(map(int, line.strip())) for line in f.readlines()] 5 | 6 | def score(board, x, y): 7 | ret = 0 8 | 9 | visited = set((x, y)) 10 | queue = deque([(x, y)]) 11 | 12 | cnt = defaultdict(int) 13 | cnt[(x, y)] = 1 14 | 15 | while queue: 16 | x, y = queue.pop() 17 | 18 | if (x, y) in visited: 19 | continue 20 | visited.add((x, y)) 21 | 22 | if board[y][x] == 9: 23 | ret += cnt[(x, y)] 24 | continue 25 | 26 | for dx, dy in (0, -1), (0, 1), (-1, 0), (1, 0): 27 | if 0 <= x + dx < len(board[0]) and 0 <= y + dy < len(board): 28 | if board[y + dy][x + dx] == board[y][x] + 1: 29 | queue.appendleft((x + dx, y + dy)) 30 | cnt[(x + dx, y + dy)] = cnt[(x + dx, y + dy)] + cnt[(x, y)] 31 | 32 | return ret 33 | 34 | tot = 0 35 | for y in range(len(board)): 36 | for x in range(len(board[0])): 37 | if board[y][x] == 0: 38 | tot += score(board, x, y) 39 | print(tot) -------------------------------------------------------------------------------- /2024/11/11a.py: -------------------------------------------------------------------------------- 1 | with open('in.txt') as f: 2 | nums = [int(num) for num in f.read().split(' ')] 3 | 4 | def evolve(nums): 5 | new = [] 6 | for num in nums: 7 | if num == 0: 8 | new.append(1) 9 | continue 10 | if len(str(num)) % 2 == 0: 11 | new.append(int(str(num)[:len(str(num)) // 2])) 12 | new.append(int(str(num)[len(str(num)) // 2:])) 13 | continue 14 | new.append(num * 2024) 15 | return new 16 | 17 | for i in range(25): 18 | nums = evolve(nums) 19 | 20 | print(len(nums)) 21 | -------------------------------------------------------------------------------- /2024/11/11b.py: -------------------------------------------------------------------------------- 1 | import functools 2 | 3 | with open('in.txt') as f: 4 | nums = [int(num) for num in f.read().split(' ')] 5 | 6 | @functools.cache 7 | def count(num, iters): 8 | if iters == 0: 9 | return 1 10 | if num == 0: 11 | return count(1, iters - 1) 12 | s = str(num) 13 | l = len(s) 14 | if l % 2 == 0: 15 | return count(int(s[:l//2]), iters - 1) + count(int(s[l//2:]), iters - 1) 16 | return count(num * 2024, iters - 1) 17 | 18 | print(sum([count(num, 75) for num in nums])) 19 | -------------------------------------------------------------------------------- /2024/12/12a.py: -------------------------------------------------------------------------------- 1 | with open('in.txt') as f: 2 | data = [list(line.strip()) for line in f] 3 | 4 | board = {} 5 | for y, line in enumerate(data): 6 | for x, c in enumerate(line): 7 | board[x + y * 1j] = c 8 | 9 | def visit(z): 10 | visited = set() 11 | frontier = [z] 12 | 13 | while frontier: 14 | z = frontier.pop() 15 | if z in visited: 16 | continue 17 | visited.add(z) 18 | 19 | for dz in 1, 1j, -1, -1j: 20 | if z + dz in board: 21 | if board[z] == board[z + dz]: 22 | frontier.append(z + dz) 23 | 24 | return visited 25 | 26 | def perimeter(region): 27 | perimeter = 0 28 | for z in region: 29 | for dz in 1, 1j, -1, -1j: 30 | if z + dz not in region: 31 | perimeter += 1 32 | return perimeter 33 | 34 | regions = [] 35 | marked = set() 36 | for z in board: 37 | if z not in marked: 38 | region = visit(z) 39 | regions.append(region) 40 | marked |= region 41 | 42 | cost = sum(perimeter(region) * len(region) for region in regions) 43 | print(cost) -------------------------------------------------------------------------------- /2024/12/12b.py: -------------------------------------------------------------------------------- 1 | from itertools import permutations 2 | 3 | with open('in.txt') as f: 4 | data = [list(line.strip()) for line in f] 5 | 6 | board = {} 7 | for y, line in enumerate(data): 8 | for x, c in enumerate(line): 9 | board[x + y * 1j] = c 10 | 11 | def visit(z): 12 | visited = set() 13 | frontier = [z] 14 | 15 | while frontier: 16 | z = frontier.pop() 17 | if z in visited: 18 | continue 19 | visited.add(z) 20 | 21 | for dz in 1, 1j, -1, -1j: 22 | if z + dz in board: 23 | if board[z + dz] == board[z]: 24 | frontier.append(z + dz) 25 | 26 | return visited 27 | 28 | def perimeter(region): 29 | sides = set() 30 | for z in region: 31 | for dz in 1j, -1j, 1, -1: 32 | if z + dz not in region: 33 | sides.add((z, z + dz * 1j, dz)) 34 | 35 | while True: 36 | for a, b in permutations(sides, 2): 37 | if a[1] == b[0] and a[2] == b[2]: 38 | c = (a[0], b[1], a[2]) 39 | sides.remove(a) 40 | sides.remove(b) 41 | sides.add(c) 42 | break 43 | else: 44 | break 45 | return len(sides) 46 | 47 | regions = [] 48 | marked = set() 49 | for z in board: 50 | if z not in marked: 51 | region = visit(z) 52 | regions.append(region) 53 | marked |= region 54 | 55 | cost = sum(perimeter(region) * len(region) for region in regions) 56 | print(cost) -------------------------------------------------------------------------------- /2024/13/13a.py: -------------------------------------------------------------------------------- 1 | from parse import parse 2 | import numpy as np 3 | from math import isclose 4 | 5 | with open('in.txt') as f: 6 | machines = f.read().split('\n\n') 7 | 8 | sum = 0 9 | for machine in machines: 10 | Ax, Ay, Bx, By, Cx, Cy = parse("Button A: X+{:d}, Y+{:d}\nButton B: X+{:d}, Y+{:d}\nPrize: X={:d}, Y={:d}", machine) 11 | A = np.array([[Ax, Bx], [Ay, By]]) 12 | B = np.array([Cx, Cy]) 13 | try: 14 | C = np.linalg.solve(A, B) 15 | except np.linalg.LinAlgError: 16 | continue 17 | if isclose(C[0], round(C[0])) and isclose(C[1], round(C[1])): 18 | sum += round(3 * C[0] + C[1]) 19 | print(sum) -------------------------------------------------------------------------------- /2024/13/13b.py: -------------------------------------------------------------------------------- 1 | from parse import parse 2 | import numpy as np 3 | from math import isclose 4 | 5 | with open('in.txt') as f: 6 | machines = f.read().split('\n\n') 7 | 8 | OFFSET = 10000000000000 9 | REL_TOL = 1e-15 10 | sum = 0 11 | for i, machine in enumerate(machines): 12 | Ax, Ay, Bx, By, Cx, Cy = parse("Button A: X+{:d}, Y+{:d}\nButton B: X+{:d}, Y+{:d}\nPrize: X={:d}, Y={:d}", machine) 13 | Cx += OFFSET 14 | Cy += OFFSET 15 | A = np.array([[Ax, Bx], [Ay, By]]) 16 | B = np.array([Cx, Cy]) 17 | try: 18 | C = np.linalg.solve(A, B) 19 | except np.linalg.LinAlgError: 20 | continue 21 | if isclose(C[0], round(C[0]), rel_tol=REL_TOL) and isclose(C[1], round(C[1]), rel_tol=REL_TOL): 22 | sum += round(3 * C[0] + C[1]) 23 | print(sum) -------------------------------------------------------------------------------- /2024/14/14a.py: -------------------------------------------------------------------------------- 1 | from parse import parse 2 | from math import prod 3 | 4 | with open('in.txt') as f: 5 | lines = f.read().splitlines() 6 | 7 | ITERATIONS = 100 8 | MAX_X = 101 9 | MAX_Y = 103 10 | 11 | pos = [] 12 | vel = [] 13 | for line in lines: 14 | px, py, vx, vy = parse("p={:d},{:d} v={:d},{:d}", line) 15 | pos.append([px, py]) 16 | vel.append([vx, vy]) 17 | 18 | for _ in range(ITERATIONS): 19 | for i in range(len(pos)): 20 | pos[i][0] += vel[i][0] 21 | pos[i][0] %= MAX_X 22 | pos[i][1] += vel[i][1] 23 | pos[i][1] %= MAX_Y 24 | 25 | quads = [0] * 4 26 | for pt in pos: 27 | if pt[0] < MAX_X // 2 and pt[1] < MAX_Y // 2: 28 | quads[0] += 1 29 | elif pt[0] < MAX_X // 2 and pt[1] > MAX_Y // 2: 30 | quads[1] += 1 31 | elif pt[0] > MAX_X // 2 and pt[1] < MAX_Y // 2: 32 | quads[2] += 1 33 | elif pt[0] > MAX_X // 2 and pt[1] > MAX_Y // 2: 34 | quads[3] += 1 35 | 36 | print(prod(quads)) -------------------------------------------------------------------------------- /2024/14/14b.py: -------------------------------------------------------------------------------- 1 | from parse import parse 2 | from math import prod 3 | 4 | with open('in.txt') as f: 5 | lines = f.read().splitlines() 6 | 7 | ITERATIONS = 1000000 8 | MAX_X = 101 9 | MAX_Y = 103 10 | 11 | pos = [] 12 | vel = [] 13 | for line in lines: 14 | px, py, vx, vy = parse("p={:d},{:d} v={:d},{:d}", line) 15 | pos.append([px, py]) 16 | vel.append([vx, vy]) 17 | 18 | for iter in range(ITERATIONS): 19 | for i in range(len(pos)): 20 | pos[i][0] += vel[i][0] 21 | pos[i][0] %= MAX_X 22 | pos[i][1] += vel[i][1] 23 | pos[i][1] %= MAX_Y 24 | 25 | marked = set() 26 | board = [] 27 | 28 | for y in range(MAX_Y): 29 | board.append([]) 30 | for x in range(MAX_X): 31 | board[y].append('.') 32 | 33 | for x, y in pos: 34 | board[y][x] = '#' 35 | 36 | max_cnt = 0 37 | for y in range(MAX_Y): 38 | for x in range(MAX_X): 39 | if (x, y) not in marked and board[y][x] == '#': 40 | visited = set() 41 | frontier = [(x, y)] 42 | while frontier: 43 | x, y = frontier.pop() 44 | if (x, y) in visited: 45 | continue 46 | visited.add((x, y)) 47 | for dx in [-1, 0, 1]: 48 | for dy in [-1, 0, 1]: 49 | nx, ny = x + dx, y + dy 50 | if 0 <= nx < MAX_X and 0 <= ny < MAX_Y: 51 | if board[ny][nx] == '#': 52 | frontier.append((nx, ny)) 53 | max_cnt = max(max_cnt, len(visited)) 54 | marked.update(visited) 55 | 56 | if max_cnt > 100: 57 | print(iter + 1) 58 | break -------------------------------------------------------------------------------- /2024/15/15a.py: -------------------------------------------------------------------------------- 1 | with open('in.txt') as f: 2 | data = f.read() 3 | 4 | board, moves = data.split('\n\n') 5 | moves = moves.strip().replace('\n', '') 6 | board = [list(row.strip()) for row in board.split('\n')] 7 | 8 | for y, row in enumerate(board): 9 | for x, cell in enumerate(row): 10 | if cell == '@': 11 | pos = (x, y) 12 | break 13 | 14 | def evolve(board, move, start): 15 | x, y = start 16 | dx, dy = { 17 | '>': (1, 0), 18 | '<': (-1, 0), 19 | 'v': (0, 1), 20 | '^': (0, -1), 21 | }[move] 22 | 23 | nx, ny = x + dx, y + dy 24 | while board[ny][nx] == 'O': 25 | nx, ny = nx + dx, ny + dy 26 | if board[ny][nx] == '#': 27 | return x, y 28 | assert board[ny][nx] == '.' 29 | board[y][x] = '.' 30 | x, y = x + dx, y + dy 31 | board[ny][nx] = 'O' 32 | board[y][x] = '@' 33 | return x, y 34 | 35 | for move in moves: 36 | pos = evolve(board, move, pos) 37 | 38 | s = 0 39 | for y, row in enumerate(board): 40 | for x, cell in enumerate(row): 41 | if cell == 'O': 42 | s += 100 * y + x 43 | print(s) 44 | -------------------------------------------------------------------------------- /2024/15/15b.py: -------------------------------------------------------------------------------- 1 | from collections import deque 2 | 3 | with open('in.txt') as f: 4 | data = f.read() 5 | 6 | board, moves = data.split('\n\n') 7 | moves = moves.strip().replace('\n', '') 8 | board = [row.strip() for row in board.split('\n')] 9 | 10 | for y, row in enumerate(board): 11 | row = list(row.replace('#', '##').replace('O', '[]').replace('.', '..').replace('@', '@.')) 12 | board[y] = row 13 | for x, cell in enumerate(row): 14 | if cell == '@': 15 | pos = (x, y) 16 | break 17 | 18 | def evolve(board, move, start): 19 | x, y = start 20 | dx, dy = { 21 | '>': (1, 0), 22 | '<': (-1, 0), 23 | 'v': (0, 1), 24 | '^': (0, -1), 25 | }[move] 26 | 27 | visited = set() 28 | push = [] 29 | frontier = deque([start]) 30 | possible = True 31 | 32 | while frontier: 33 | x, y = frontier.pop() 34 | 35 | if (x, y) in visited: 36 | continue 37 | if not (0 <= x < len(board[0]) and 0 <= y < len(board)): 38 | continue 39 | 40 | visited.add((x, y)) 41 | push.append((x, y)) 42 | 43 | if board[y][x] == '#': 44 | possible = False 45 | break 46 | if board[y][x] == '[': 47 | frontier.appendleft((x + 1, y)) 48 | if board[y][x] == ']': 49 | frontier.appendleft((x - 1, y)) 50 | if board[y][x] != '.': 51 | frontier.appendleft((x + dx, y + dy)) 52 | 53 | if possible: 54 | for x, y in push[::-1]: 55 | if board[y][x] != '.': 56 | board[y + dy][x + dx] = board[y][x] 57 | board[y][x] = '.' 58 | return (x + dx, y + dy) 59 | return start 60 | 61 | for move in moves: 62 | pos = evolve(board, move, pos) 63 | 64 | s = 0 65 | for y, row in enumerate(board): 66 | for x, cell in enumerate(row): 67 | if cell == '[': 68 | s += 100 * y + x 69 | print(s) 70 | -------------------------------------------------------------------------------- /2024/16/16a.py: -------------------------------------------------------------------------------- 1 | from heapq import heappush, heappop 2 | from collections import defaultdict 3 | 4 | with open('sample2.txt') as f: 5 | data = f.readlines() 6 | 7 | board = {} 8 | for y, line in enumerate(data): 9 | for x, c in enumerate(line): 10 | board[x + y * 1j] = c 11 | if c == 'S': 12 | start = x + y * 1j 13 | 14 | def dijkstra(start, v): 15 | locs = [(start, v)] 16 | frontier = [(0, 0)] 17 | costs = defaultdict(lambda: float('inf')) 18 | 19 | while frontier: 20 | cost, i = heappop(frontier) 21 | pos, v = locs[i] 22 | 23 | if cost > costs[(pos, v)]: 24 | continue 25 | costs[(pos, v)] = cost 26 | 27 | if board[pos] == 'E': 28 | return cost 29 | 30 | npos = pos + v 31 | if npos in board: 32 | if board[npos] in ('.', 'E'): 33 | heappush(frontier, (cost + 1, len(locs))) 34 | locs.append((npos, v)) 35 | 36 | for dv in 1j, -1j: 37 | heappush(frontier, (cost + 1000, len(locs))) 38 | locs.append((pos, dv * v)) 39 | 40 | print(dijkstra(start, 1)) -------------------------------------------------------------------------------- /2024/16/16b.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | from heapq import heappush, heappop 3 | 4 | with open('in.txt') as f: 5 | data = f.readlines() 6 | 7 | board = {} 8 | for y, line in enumerate(data): 9 | for x, c in enumerate(line): 10 | board[x + y * 1j] = c 11 | if c == 'S': 12 | start = x + y * 1j 13 | elif c == 'E': 14 | end = x + y * 1j 15 | 16 | def dijkstra(start, v): 17 | locs = [(start, v)] 18 | frontier = [(0, 0, None)] 19 | parents = defaultdict(list) 20 | costs = defaultdict(lambda: float('inf')) 21 | 22 | while frontier: 23 | cost, i, parent = heappop(frontier) 24 | pos, v = locs[i] 25 | 26 | if cost > costs[(pos, v)]: 27 | continue 28 | 29 | if parent: 30 | parents[(pos, v)].append(parent) 31 | 32 | costs[(pos, v)] = cost 33 | 34 | if board[pos] == 'E': 35 | continue 36 | 37 | npos = pos + v 38 | if npos in board: 39 | if board[npos] in ('.', 'E'): 40 | heappush(frontier, (cost + 1, len(locs), (pos, v))) 41 | locs.append((npos, v)) 42 | 43 | for dv in 1j, -1j: 44 | heappush(frontier, (cost + 1000, len(locs), (pos, v))) 45 | locs.append((pos, dv * v)) 46 | 47 | tiles = set() 48 | pending = [] 49 | best_cost = float('inf') 50 | for v in 1, -1, 1j, -1j: 51 | if costs[(end, v)] < best_cost: 52 | best_cost = costs[(end, v)] 53 | for v in 1, -1, 1j, -1j: 54 | if costs[(end, v)] == best_cost: 55 | pending.append((end, v)) 56 | 57 | visited = set() 58 | while pending: 59 | pos, v = pending.pop() 60 | if (pos, v) in visited: 61 | continue 62 | visited.add((pos, v)) 63 | tiles.add(pos) 64 | 65 | for parent in parents[(pos, v)]: 66 | pending.append(parent) 67 | 68 | return tiles 69 | 70 | print(len(dijkstra(start, 1))) -------------------------------------------------------------------------------- /2024/18/18a.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict, deque 2 | 3 | with open('in.txt') as f: 4 | lines = f.readlines()[:1024] 5 | 6 | board = defaultdict(lambda: '.') 7 | W = H = 70 + 1 8 | 9 | for line in lines: 10 | x, y = [int(i) for i in line.split(',')] 11 | board[x + y * 1j] = '#' 12 | 13 | s = 0 14 | t = W - 1 + (H - 1) * 1j 15 | 16 | def bfs(s, t): 17 | q = deque([(s, 0)]) 18 | visited = set() 19 | while q: 20 | pt, dist = q.pop() 21 | if pt in visited: 22 | continue 23 | visited.add(pt) 24 | if pt == t: 25 | return dist 26 | for delta in 1, -1, 1j, -1j: 27 | n = pt + delta 28 | if 0 <= n.real < W and 0 <= n.imag < H: 29 | if board[n] == '.': 30 | q.appendleft((n, dist + 1)) 31 | return None 32 | 33 | print(bfs(s, t)) -------------------------------------------------------------------------------- /2024/18/18b.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict, deque 2 | 3 | W = H = 70 + 1 4 | with open('in.txt') as f: 5 | lines = f.readlines() 6 | points = list(tuple(int(i) for i in line.split(',')) for line in lines) 7 | 8 | def reachable(obstacles): 9 | board = defaultdict(lambda: '.') 10 | for pt in obstacles: 11 | x, y = pt 12 | board[x + y * 1j] = '#' 13 | 14 | def bfs(s, t): 15 | q = deque([s]) 16 | visited = set() 17 | while q: 18 | pt = q.pop() 19 | if pt in visited: 20 | continue 21 | visited.add(pt) 22 | if pt == t: 23 | return True 24 | for delta in 1, -1, 1j, -1j: 25 | n = pt + delta 26 | if 0 <= n.real < W and 0 <= n.imag < H and board[n] == '.': 27 | q.appendleft(n) 28 | return False 29 | 30 | return bfs(0, W - 1 + (H - 1) * 1j) 31 | 32 | # invariant: first unreachable point pt is in [lo, hi) 33 | lo, hi = 0, len(points) 34 | while lo < hi: 35 | mid = (lo + hi) // 2 36 | if reachable(points[:mid]): 37 | lo = mid + 1 38 | else: 39 | hi = mid 40 | assert reachable(points[:lo - 1]) and not reachable(points[:lo]) 41 | 42 | print(','.join(map(str, points[lo - 1]))) -------------------------------------------------------------------------------- /2024/23/23a.py: -------------------------------------------------------------------------------- 1 | from itertools import product 2 | from collections import defaultdict 3 | from pprint import pprint 4 | 5 | with open('in.txt') as f: 6 | connections = [line.strip().split('-') for line in f.readlines()] 7 | 8 | nodes = set() 9 | G = defaultdict(list) 10 | for left, right in connections: 11 | nodes.add(left) 12 | nodes.add(right) 13 | G[left].append(right) 14 | G[right].append(left) 15 | 16 | head_nodes = [node for node in nodes if node[0] == 't'] 17 | 18 | sets = set() 19 | for v in head_nodes: 20 | for u in G[v]: 21 | for w in G[u]: 22 | if w != v and v in G[w]: 23 | sets.add(frozenset([v, u, w])) 24 | 25 | pprint(len(sets)) 26 | -------------------------------------------------------------------------------- /2024/23/23b.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | 3 | with open('in.txt') as f: 4 | connections = [line.strip().split('-') for line in f.readlines()] 5 | 6 | nodes = set() 7 | G = defaultdict(set) 8 | for left, right in connections: 9 | nodes.add(left) 10 | nodes.add(right) 11 | if left < right: 12 | G[left].add(right) 13 | else: 14 | G[right].add(left) 15 | 16 | def clique(fixed, candidates, k): 17 | if len(fixed) == k: 18 | return fixed 19 | for candidate in candidates: 20 | result = clique([*fixed, candidate], candidates & G[candidate], k) 21 | if result: 22 | return result 23 | return None 24 | 25 | MAX_K = 20 26 | best_clique = [] 27 | for k in range(1, MAX_K + 1): 28 | for v in nodes: 29 | fixed = [v] 30 | candidates = G[v] 31 | result = clique(fixed, candidates, k) 32 | if result: 33 | best_clique = result 34 | break 35 | else: 36 | break 37 | 38 | print(','.join(best_clique)) --------------------------------------------------------------------------------