Removing files from data analysis directory
[ou-summer-of-code-2017.git] / 02-lifts / lifts-solution.ipynb
1 {
2 "cells": [
3 {
4 "cell_type": "markdown",
5 "metadata": {},
6 "source": [
7 "# Puzzle 2: Lifts\n",
8 "\n",
9 "## Part 1: using the lifts\n",
10 "\n",
11 "You've chosen your holiday, now it's time to catch your flight. You arrive at Heathwick airport and have to find the departures floor. However, getting around Terminal $\\pi$ is a real pain. The instructions for how to use the lifts are rather complex.\n",
12 "\n",
13 "You start in the car park (the basement level, floor 0) and follow this instructions one step at a time. The instructions are each one character:\n",
14 "* `^` : go up one floor\n",
15 "* `v` : go down one floor\n",
16 "* `=` : open the doors, without moving floors.\n",
17 "\n",
18 "The input contains no other characters.\n",
19 "\n",
20 "If you follow the instructions and get out at the end, what floor do you end up on?\n",
21 "\n",
22 "Terminal $\\pi$ is large and there's no limit to the number of floors, both up and down.\n",
23 "\n",
24 "For instance, the sequence '^=' takes you to floor 1. \n",
25 "\n",
26 "The sequence `vv^=` takes you down two floors to the sub-sub-basement (floor -2), then up one floor, and you get out in the sub-basement on floor -1.\n",
27 "\n",
28 "The sequence `^^v=^=` would start on floor 0, go up two floors, then down one. The doors would then open on floor 1, but you'd stay in the lift. You'd then move up to floor 2, the doors open, and you get out on floor 2. \n",
29 "\n",
30 "The sequence `v^^^^^v=v=` would go down one floor (to floor -1), up five floors (to floor 4), down one floor (to floor 3), open the doors, go down another floor, and you'd finally get out at floor 2.\n",
31 "\n",
32 "Given the input in [02-lifts.txt](02-lifts.txt), where would you get out?"
33 ]
34 },
35 {
36 "cell_type": "markdown",
37 "metadata": {},
38 "source": [
39 "# Worked example of solution: Part 1\n",
40 "\n",
41 "This is an example of a very common pattern: walking along a list, finding some total or final value. As such, there are a great many ways of doing it.\n",
42 "\n",
43 "The most obvious way is to have a variable that holds the current floor, and update it with every instruction that's read from the list of instructions. As the current floor is just a number, we can have a simple variable.\n",
44 "\n",
45 "The list of instructions will be read as a string, and we can iterate through a string easily, so we'll leave the instructions as a string. Happily, each instruction is one character, so no need to count characters and instructions separately.\n",
46 "\n",
47 "The only complication is that there's no obvious trick for converting the instruction characters. Given that, let's just do a simple `if`-`elisf`-`else` structure (or a `case` statement of your language of choice has it) to convert an instruction into a floor change. \n",
48 "\n",
49 "(I could also have used a `dict` like \n",
50 "\n",
51 "```\n",
52 "value = {'^': 1, 'v': -1, '=': 0}\n",
53 "```\n",
54 "\n",
55 "and it would behave in the same way.)\n",
56 "\n",
57 "(Another common solution was to count the number of `^` characters and the number of `v` characters, subtract one from the other, and give the result. That works for part 1 but doesn't work for part 2!)"
58 ]
59 },
60 {
61 "cell_type": "code",
62 "execution_count": 2,
63 "metadata": {
64 "collapsed": true
65 },
66 "outputs": [],
67 "source": [
68 "def value(instr):\n",
69 " if instr == '^':\n",
70 " return 1\n",
71 " elif instr == 'v':\n",
72 " return -1\n",
73 " else:\n",
74 " return 0"
75 ]
76 },
77 {
78 "cell_type": "markdown",
79 "metadata": {},
80 "source": [
81 "Now we can find the floor-change of each instruction, we set the initial floor to 0 and run down the list, updating the current floor at each step."
82 ]
83 },
84 {
85 "cell_type": "code",
86 "execution_count": 3,
87 "metadata": {
88 "collapsed": true
89 },
90 "outputs": [],
91 "source": [
92 "def final(sequence):\n",
93 " current = 0\n",
94 " for c in sequence:\n",
95 " current += value(c)\n",
96 " return current"
97 ]
98 },
99 {
100 "cell_type": "markdown",
101 "metadata": {},
102 "source": [
103 "Finally, open the file of instructions and find the final floor."
104 ]
105 },
106 {
107 "cell_type": "code",
108 "execution_count": 4,
109 "metadata": {},
110 "outputs": [
111 {
112 "data": {
113 "text/plain": [
114 "209"
115 ]
116 },
117 "execution_count": 4,
118 "metadata": {},
119 "output_type": "execute_result"
120 }
121 ],
122 "source": [
123 "with open('02-lifts.txt') as f:\n",
124 " instructions = f.read()\n",
125 " exit = final(instructions)\n",
126 "exit"
127 ]
128 },
129 {
130 "cell_type": "markdown",
131 "metadata": {},
132 "source": [
133 "(For my testing purposes, find all the floors visited so I can give sensible hints for wrong answers.)"
134 ]
135 },
136 {
137 "cell_type": "code",
138 "execution_count": 5,
139 "metadata": {
140 "collapsed": true
141 },
142 "outputs": [],
143 "source": [
144 "def running(sequence):\n",
145 " current = 0\n",
146 " floors = []\n",
147 " for i in sequence:\n",
148 " current += value(i)\n",
149 " floors += [current]\n",
150 " return floors"
151 ]
152 },
153 {
154 "cell_type": "code",
155 "execution_count": 6,
156 "metadata": {},
157 "outputs": [
158 {
159 "data": {
160 "text/plain": [
161 "(10002, 216, -6)"
162 ]
163 },
164 "execution_count": 6,
165 "metadata": {},
166 "output_type": "execute_result"
167 }
168 ],
169 "source": [
170 "with open('02-lifts.txt') as f:\n",
171 " instructions = f.read()\n",
172 " floors = running(instructions)\n",
173 "len(floors), max(floors), min(floors)"
174 ]
175 },
176 {
177 "cell_type": "markdown",
178 "metadata": {},
179 "source": [
180 "### Smart-alec one line solution"
181 ]
182 },
183 {
184 "cell_type": "code",
185 "execution_count": 7,
186 "metadata": {},
187 "outputs": [
188 {
189 "data": {
190 "text/plain": [
191 "209"
192 ]
193 },
194 "execution_count": 7,
195 "metadata": {},
196 "output_type": "execute_result"
197 }
198 ],
199 "source": [
200 "sum(value(i) for i in open('02-lifts.txt').read())"
201 ]
202 },
203 {
204 "cell_type": "markdown",
205 "metadata": {},
206 "source": [
207 "## Part 2: getting out\n",
208 "You can only leave the lift where the doors are open. What is the highest floor you could leave from?\n",
209 "\n",
210 "For instance, the sequence `^^v=^=` would allow you to exit on floors 1 and 2, so the highest floor you could leave from would be floor 2. \n",
211 "\n",
212 "The sequence `v^^^^^v=v=` would allow you to exit on floors 3 and 2, so the highest floor you could leave from would be floor 3 (even though the lift reaches floor 4)."
213 ]
214 },
215 {
216 "cell_type": "markdown",
217 "metadata": {},
218 "source": [
219 "# Worked example of solution: part 2\n",
220 "This is another common pattern: walking down a list, keeping some extreme value (the largest, the smallest, or something like that.)\n",
221 "\n",
222 "The basic idea is to keep a variable holding the highest exit seen so far, and update it if we see a higher exit. We start with some initial value, such as zero. \n",
223 "\n",
224 "The `highest_exit` function is very similar to the `final` function, but with the `if` statement in the loop. This just checks if we're at an exit and it's higher than what we've seen so far; if so, it updates the `highest_exit` variable.\n",
225 "\n",
226 "It was pointed out in the forums that by starting with the `highest_exit` at 0, we're assuming that the highest exit is above ground. That's true in this case, but its a valid concern. In this case, I should set the initial value to be smaller than the smallest valid value we can find, so that the first exit we find is correctly recorded. In this case, the smallest value for the exit is if all the instructions are down, or -1 × (number of instructions)."
227 ]
228 },
229 {
230 "cell_type": "code",
231 "execution_count": 22,
232 "metadata": {
233 "collapsed": true
234 },
235 "outputs": [],
236 "source": [
237 "def highest_exit(sequence):\n",
238 " highest_exit = -1 - len(sequence) # or 0\n",
239 " current = 0\n",
240 " for i in sequence:\n",
241 " if value(i) == 0 and current > highest_exit:\n",
242 " highest_exit = current\n",
243 " else:\n",
244 " current += value(i)\n",
245 " return highest_exit"
246 ]
247 },
248 {
249 "cell_type": "code",
250 "execution_count": 21,
251 "metadata": {},
252 "outputs": [
253 {
254 "data": {
255 "text/plain": [
256 "215"
257 ]
258 },
259 "execution_count": 21,
260 "metadata": {},
261 "output_type": "execute_result"
262 }
263 ],
264 "source": [
265 "highest_exit(instructions)"
266 ]
267 },
268 {
269 "cell_type": "markdown",
270 "metadata": {},
271 "source": [
272 "A variation that returns a list of all the exits, so I can generate hints for obvious wrong answers (the last exit, the lowest exit, the first exit, the number of exits)."
273 ]
274 },
275 {
276 "cell_type": "code",
277 "execution_count": 10,
278 "metadata": {
279 "collapsed": true
280 },
281 "outputs": [],
282 "source": [
283 "def exits(sequence):\n",
284 " current = 0\n",
285 " exits = []\n",
286 " for i in sequence:\n",
287 " if value(i) == 0:\n",
288 " exits.append(current)\n",
289 " else:\n",
290 " current += value(i)\n",
291 " return exits"
292 ]
293 },
294 {
295 "cell_type": "code",
296 "execution_count": 11,
297 "metadata": {},
298 "outputs": [
299 {
300 "data": {
301 "text/plain": [
302 "215"
303 ]
304 },
305 "execution_count": 11,
306 "metadata": {},
307 "output_type": "execute_result"
308 }
309 ],
310 "source": [
311 "max(exits(instructions))"
312 ]
313 },
314 {
315 "cell_type": "code",
316 "execution_count": 12,
317 "metadata": {},
318 "outputs": [
319 {
320 "data": {
321 "text/plain": [
322 "-5"
323 ]
324 },
325 "execution_count": 12,
326 "metadata": {},
327 "output_type": "execute_result"
328 }
329 ],
330 "source": [
331 "min(exits(instructions))"
332 ]
333 },
334 {
335 "cell_type": "code",
336 "execution_count": 13,
337 "metadata": {},
338 "outputs": [
339 {
340 "data": {
341 "text/plain": [
342 "209"
343 ]
344 },
345 "execution_count": 13,
346 "metadata": {},
347 "output_type": "execute_result"
348 }
349 ],
350 "source": [
351 "exits(instructions)[-1]"
352 ]
353 },
354 {
355 "cell_type": "code",
356 "execution_count": 14,
357 "metadata": {},
358 "outputs": [
359 {
360 "data": {
361 "text/plain": [
362 "-2"
363 ]
364 },
365 "execution_count": 14,
366 "metadata": {},
367 "output_type": "execute_result"
368 }
369 ],
370 "source": [
371 "exits(instructions)[0]"
372 ]
373 },
374 {
375 "cell_type": "code",
376 "execution_count": 15,
377 "metadata": {},
378 "outputs": [
379 {
380 "data": {
381 "text/plain": [
382 "1259"
383 ]
384 },
385 "execution_count": 15,
386 "metadata": {},
387 "output_type": "execute_result"
388 }
389 ],
390 "source": [
391 "len(exits(instructions))"
392 ]
393 },
394 {
395 "cell_type": "code",
396 "execution_count": 16,
397 "metadata": {},
398 "outputs": [
399 {
400 "data": {
401 "text/plain": [
402 "Counter({-5: 1,\n",
403 " -4: 1,\n",
404 " -2: 2,\n",
405 " 1: 1,\n",
406 " 3: 1,\n",
407 " 6: 1,\n",
408 " 11: 1,\n",
409 " 12: 1,\n",
410 " 18: 1,\n",
411 " 19: 2,\n",
412 " 20: 3,\n",
413 " 21: 3,\n",
414 " 22: 4,\n",
415 " 23: 4,\n",
416 " 24: 6,\n",
417 " 25: 4,\n",
418 " 26: 4,\n",
419 " 27: 6,\n",
420 " 28: 4,\n",
421 " 29: 4,\n",
422 " 30: 2,\n",
423 " 31: 1,\n",
424 " 32: 7,\n",
425 " 33: 3,\n",
426 " 34: 5,\n",
427 " 35: 2,\n",
428 " 36: 2,\n",
429 " 37: 2,\n",
430 " 38: 3,\n",
431 " 39: 5,\n",
432 " 40: 9,\n",
433 " 41: 4,\n",
434 " 42: 7,\n",
435 " 43: 11,\n",
436 " 44: 15,\n",
437 " 45: 19,\n",
438 " 46: 22,\n",
439 " 47: 12,\n",
440 " 48: 23,\n",
441 " 49: 24,\n",
442 " 50: 27,\n",
443 " 51: 25,\n",
444 " 52: 27,\n",
445 " 53: 25,\n",
446 " 54: 16,\n",
447 " 55: 21,\n",
448 " 56: 21,\n",
449 " 57: 15,\n",
450 " 58: 13,\n",
451 " 59: 18,\n",
452 " 60: 19,\n",
453 " 61: 13,\n",
454 " 62: 21,\n",
455 " 63: 15,\n",
456 " 64: 19,\n",
457 " 65: 18,\n",
458 " 66: 17,\n",
459 " 67: 17,\n",
460 " 68: 8,\n",
461 " 69: 8,\n",
462 " 70: 12,\n",
463 " 71: 6,\n",
464 " 72: 5,\n",
465 " 73: 3,\n",
466 " 74: 6,\n",
467 " 75: 5,\n",
468 " 76: 3,\n",
469 " 77: 2,\n",
470 " 78: 1,\n",
471 " 79: 4,\n",
472 " 80: 2,\n",
473 " 81: 1,\n",
474 " 83: 1,\n",
475 " 84: 4,\n",
476 " 87: 3,\n",
477 " 88: 4,\n",
478 " 89: 2,\n",
479 " 90: 2,\n",
480 " 91: 3,\n",
481 " 92: 1,\n",
482 " 93: 5,\n",
483 " 94: 2,\n",
484 " 95: 6,\n",
485 " 96: 5,\n",
486 " 97: 11,\n",
487 " 98: 10,\n",
488 " 99: 15,\n",
489 " 100: 10,\n",
490 " 101: 17,\n",
491 " 102: 15,\n",
492 " 103: 6,\n",
493 " 104: 9,\n",
494 " 105: 6,\n",
495 " 106: 8,\n",
496 " 107: 13,\n",
497 " 108: 12,\n",
498 " 109: 9,\n",
499 " 110: 10,\n",
500 " 111: 7,\n",
501 " 112: 4,\n",
502 " 113: 8,\n",
503 " 114: 13,\n",
504 " 115: 6,\n",
505 " 116: 6,\n",
506 " 117: 6,\n",
507 " 118: 6,\n",
508 " 119: 7,\n",
509 " 120: 4,\n",
510 " 121: 3,\n",
511 " 122: 5,\n",
512 " 123: 8,\n",
513 " 124: 6,\n",
514 " 125: 7,\n",
515 " 126: 6,\n",
516 " 127: 4,\n",
517 " 128: 3,\n",
518 " 129: 5,\n",
519 " 130: 6,\n",
520 " 131: 6,\n",
521 " 132: 8,\n",
522 " 133: 5,\n",
523 " 134: 13,\n",
524 " 135: 13,\n",
525 " 136: 14,\n",
526 " 137: 9,\n",
527 " 138: 16,\n",
528 " 139: 12,\n",
529 " 140: 12,\n",
530 " 141: 11,\n",
531 " 142: 15,\n",
532 " 143: 16,\n",
533 " 144: 3,\n",
534 " 145: 5,\n",
535 " 146: 10,\n",
536 " 147: 3,\n",
537 " 148: 3,\n",
538 " 149: 2,\n",
539 " 150: 3,\n",
540 " 151: 6,\n",
541 " 152: 7,\n",
542 " 153: 5,\n",
543 " 154: 7,\n",
544 " 155: 5,\n",
545 " 156: 5,\n",
546 " 157: 1,\n",
547 " 158: 5,\n",
548 " 159: 5,\n",
549 " 160: 6,\n",
550 " 161: 2,\n",
551 " 162: 2,\n",
552 " 164: 7,\n",
553 " 165: 2,\n",
554 " 166: 2,\n",
555 " 167: 3,\n",
556 " 168: 1,\n",
557 " 169: 4,\n",
558 " 170: 3,\n",
559 " 171: 1,\n",
560 " 173: 1,\n",
561 " 178: 1,\n",
562 " 179: 1,\n",
563 " 181: 1,\n",
564 " 185: 1,\n",
565 " 187: 1,\n",
566 " 188: 1,\n",
567 " 189: 1,\n",
568 " 192: 2,\n",
569 " 193: 1,\n",
570 " 194: 3,\n",
571 " 195: 1,\n",
572 " 196: 2,\n",
573 " 199: 1,\n",
574 " 200: 2,\n",
575 " 201: 1,\n",
576 " 204: 1,\n",
577 " 205: 1,\n",
578 " 207: 2,\n",
579 " 208: 3,\n",
580 " 209: 11,\n",
581 " 210: 2,\n",
582 " 211: 1,\n",
583 " 212: 1,\n",
584 " 213: 2,\n",
585 " 215: 2})"
586 ]
587 },
588 "execution_count": 16,
589 "metadata": {},
590 "output_type": "execute_result"
591 }
592 ],
593 "source": [
594 "import collections\n",
595 "collections.Counter(exits(instructions))"
596 ]
597 },
598 {
599 "cell_type": "code",
600 "execution_count": 17,
601 "metadata": {},
602 "outputs": [
603 {
604 "data": {
605 "text/plain": [
606 "-2"
607 ]
608 },
609 "execution_count": 17,
610 "metadata": {},
611 "output_type": "execute_result"
612 }
613 ],
614 "source": [
615 "max(exits(instructions[:20]))"
616 ]
617 },
618 {
619 "cell_type": "code",
620 "execution_count": 18,
621 "metadata": {},
622 "outputs": [
623 {
624 "data": {
625 "text/plain": [
626 "(209, 215)"
627 ]
628 },
629 "execution_count": 18,
630 "metadata": {},
631 "output_type": "execute_result"
632 }
633 ],
634 "source": [
635 "import functools\n",
636 "ds = {'^': +1, 'v': -1, '=': 0}\n",
637 "def s(p, c): return p[0] + ds[c], p[0] if c == '=' and p[0] > p[1] else p[1]\n",
638 "functools.reduce(s, open('02-lifts.txt').read().strip(), (0, float('-inf')))"
639 ]
640 },
641 {
642 "cell_type": "code",
643 "execution_count": 19,
644 "metadata": {},
645 "outputs": [
646 {
647 "data": {
648 "text/plain": [
649 "(209, 215)"
650 ]
651 },
652 "execution_count": 19,
653 "metadata": {},
654 "output_type": "execute_result"
655 }
656 ],
657 "source": [
658 "import functools\n",
659 "ds = {'^': +1, 'v': -1, '=': 0}\n",
660 "functools.reduce(lambda p, c: (p[0] + ds[c], p[0] if c == '=' and p[0] > p[1] else p[1]), open('02-lifts.txt').read().strip(), (0, 0))"
661 ]
662 },
663 {
664 "cell_type": "code",
665 "execution_count": null,
666 "metadata": {
667 "collapsed": true
668 },
669 "outputs": [],
670 "source": []
671 }
672 ],
673 "metadata": {
674 "kernelspec": {
675 "display_name": "Python 3",
676 "language": "python",
677 "name": "python3"
678 },
679 "language_info": {
680 "codemirror_mode": {
681 "name": "ipython",
682 "version": 3
683 },
684 "file_extension": ".py",
685 "mimetype": "text/x-python",
686 "name": "python",
687 "nbconvert_exporter": "python",
688 "pygments_lexer": "ipython3",
689 "version": "3.5.2+"
690 }
691 },
692 "nbformat": 4,
693 "nbformat_minor": 2
694 }