├── .gitignore ├── 00_CompletionStats.ipynb ├── 2014_01_SumOfSquares.ipynb ├── 2014_02_Hooks.ipynb ├── 2014_05_TileAndTrouble.ipynb ├── 2014_06_QuestionMark.ipynb ├── 2014_07_ChainReaction.ipynb ├── 2014_08_NumberCross.ipynb ├── 2014_10_MineSweeping.ipynb ├── 2014_11_WrongDivision.ipynb ├── 2015_05_TicTacOh.ipynb ├── 2015_06_Polymath.ipynb ├── 2015_07_GoodChemistry.ipynb ├── 2015_08_NumberCross2.ipynb ├── 2015_09_ThinkOfTheChildren.ipynb ├── 2015_10_HexAgony.ipynb ├── 2015_11_PairDance.ipynb ├── 2015_12_ProfessorRando.ipynb ├── 2016_01_ProfessorRandoRedux.ipynb ├── 2016_02_TravelAgent.ipynb ├── 2016_03_KnightMoves-Z3.ipynb ├── 2016_03_KnightMoves.ipynb ├── 2016_05_Hooks2-z3.ipynb ├── 2016_05_Hooks2.ipynb ├── 2016_06_GetOutTheVote.ipynb ├── 2016_07_NumberCross3.ipynb ├── 2016_08_SwingTime.ipynb ├── 2016_09_ChessDance.ipynb ├── 2016_10_TriTriAgain.ipynb ├── 2017_01_HexAgony2.ipynb ├── 2017_02_WhatAboutBob.ipynb ├── 2017_03_BirthdayBash.ipynb ├── 2017_04_KnightMoves2-Z3.ipynb ├── 2017_04_KnightMoves2.ipynb ├── 2017_05_KenKen(Concatenated).ipynb ├── 2017_06_WellWellWell.ipynb ├── 2017_07_SplitDivision-Improved.ipynb ├── 2017_07_SplitDivision.ipynb ├── 2017_09_SquareRun.ipynb ├── 2017_11_BlockParty-Z3.ipynb ├── 2017_11_BlockParty.ipynb ├── 2018_01_RatherSquareSudoku-Z3.ipynb ├── 2018_01_RatherSquareSudoku.ipynb ├── 2018_02_Hooks3-z3.ipynb ├── 2018_02_Hooks3.ipynb ├── 2018_03_ItsSymmetric.ipynb ├── 2018_05_SwingTime2.ipynb ├── 2018_06_TwentyFourSeven.ipynb ├── 2018_06_TwentyFourSeven_Z3.ipynb ├── 2018_08_Hooks4-z3.ipynb ├── 2018_08_Hooks4.ipynb ├── 2018_09_SpiralRegion.ipynb ├── 2018_10_Subtiles.ipynb ├── 2018_11_PentUpFrustration.ipynb ├── 2018_12_BlockParty2-z3.ipynb ├── 2018_12_BlockParty2.ipynb ├── 2019_01_Fences.ipynb ├── 2019_03_TwentyFourSeven2by2-Z3.ipynb ├── 2019_03_TwentyFourSeven2by2.ipynb ├── 2019_04_Remote Sudoku.ipynb ├── 2019_04_RemoteSudoku-Z3.ipynb ├── 2019_06_Hooks5-z3.ipynb ├── 2019_06_Hooks5.ipynb ├── 2019_08_KnightMoves3-Z3.ipynb ├── 2019_08_KnightMoves3.ipynb ├── 2019_09_BlockParty3-Z3.ipynb ├── 2019_09_BlockParty3.ipynb ├── 2019_10_TriTriAgainAgain.ipynb ├── 2019_11_Hooks6-z3.ipynb ├── 2019_11_Hooks6.ipynb ├── 2020_01_AlterNate.ipynb ├── 2020_05_Expelled.ipynb ├── 2020_07_WhatATrit.ipynb ├── 2020_08_StudyAndPonder.ipynb ├── 2020_09_Tangled.ipynb ├── 2020_10&11_CandyCollectors.ipynb ├── 2020_12_TwentyFourSeven2by2_2-z3.ipynb ├── 2020_12_TwentyFourSeven2by2_2.ipynb ├── 2021_01_FigurineFiguring.ipynb ├── 2021_02_Hooks7-z3.ipynb ├── 2021_02_Hooks7.ipynb ├── 2021_04_Bracketology101.ipynb ├── 2021_05_PastTens.ipynb ├── 2021_06_RobotWeightlifting.ipynb ├── 2021_07_ItsSymmetric2.ipynb ├── 2021_08_RobotTugOfWar(SageMath).ipynb ├── 2021_08_RobotTugOfWar(python).ipynb ├── 2021_09_KnightMoves4.ipynb ├── 2021_10_RobotSwimmingTrials.ipynb ├── 2021_11_SplitDivision2.ipynb ├── 2021_12_RobotArchery.ipynb ├── 2022_01_Hooks8-z3.ipynb ├── 2022_01_Hooks8.ipynb ├── 2022_02_Eldrow.ipynb ├── 2022_04_AlmostMagic.ipynb ├── 2022_05_Robot_Updated_Swimming_Trials.ipynb ├── 2022_06_BlockParty4.ipynb ├── 2022_07_AndysMorningStroll.ipynb ├── 2022_08_NewYorkMinute.ipynb ├── 2022_10_TheMarshyMess.ipynb ├── 2022_11_PentUpFrustration2.ipynb ├── 2022_12_DieAgony.ipynb ├── 2023_01_Lesses_More.ipynb ├── 2023_02_TwentyFourSeven_FourInOne.ipynb ├── 2023_03_Robot_Long_Jump.ipynb ├── 2023_04_Arc_edge_Acreage.ipynb ├── 2023_05_Game_Night.ipynb ├── 2023_06_Hooks9-z3.ipynb ├── 2023_06_Hooks9.ipynb ├── 2023_07_ChocoBanana-z3.ipynb ├── 2023_07_ChocoBanana.ipynb ├── 2023_08_SingleCross2.ipynb ├── 2023_09_Getting_from_a_to_b.ipynb ├── 2023_10_a_weird_tour.ipynb ├── 2023_11_knight_moves_5.ipynb ├── 2023_12_HallOfMirrors2.ipynb ├── 2024_01_SomeFSquares.ipynb ├── 2024_02_Some Off Square.ipynb ├── 2024_03_Hooks10.ipynb ├── 2024_04_RobotCaptureTheFlag.ipynb ├── 2024_05_NumberCross4.ipynb ├── 2024_06_AlteredStates2.ipynb ├── 2024_08_TreeEdgeTriage.ipynb ├── 2024_09_Fences-2.ipynb ├── 2024_10_knight_moves_6.ipynb ├── 2024_11_BesideThePoint.ipynb ├── 2025_01_SomewhatSquareSudoku.ipynb ├── 2025_03_HallOfMirrors3.ipynb ├── 2025_04_SumOneSomewhere-.ipynb ├── 2025_05_NumberCross5.ipynb ├── EldrowWordlist.csv ├── Numberphile2023.ipynb ├── Path.jpg ├── README.md ├── Semaphore.jpg ├── cbanana_raw.txt ├── cbanana_solved.txt └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | -------------------------------------------------------------------------------- /00_CompletionStats.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "id": "b8335901-bf75-4944-95c7-7ab5e908db29", 7 | "metadata": { 8 | "tags": [] 9 | }, 10 | "outputs": [], 11 | "source": [ 12 | "import requests\n", 13 | "from bs4 import BeautifulSoup,SoupStrainer\n", 14 | "import pandas as pd\n", 15 | "from IPython.display import Markdown, display,HTML,Image\n", 16 | "from selenium import webdriver\n", 17 | "import time\n", 18 | "from pandas.plotting import table \n", 19 | "import matplotlib.pyplot as plt\n", 20 | "import string\n", 21 | "\n", 22 | "# Borrowed the offset idea from this (much more professionally done) tableau page\n", 23 | "# https://public.tableau.com/app/profile/heidi.stockton/viz/PuzzlesofJaneStreet/JaneStreet\n", 24 | "# No adjustments for similar names etc (apologies)" 25 | ] 26 | }, 27 | { 28 | "cell_type": "code", 29 | "execution_count": 2, 30 | "id": "94944254-3f39-47c8-bf21-5e47e3e01e64", 31 | "metadata": { 32 | "tags": [] 33 | }, 34 | "outputs": [], 35 | "source": [ 36 | "# get the solution links\n", 37 | "pages = ['https://www.janestreet.com/puzzles/archive/index.html']+['https://www.janestreet.com/puzzles/archive/page'+str(i)+'/index.html' for i in range(2,13)]\n", 38 | "links = ['https://www.janestreet.com/puzzles/current-puzzle/']\n", 39 | "for url in pages:\n", 40 | " res = requests.get(url)\n", 41 | " soup = BeautifulSoup(res.content, 'html.parser')\n", 42 | " links += ['https://www.janestreet.com/'+i['href'] for i in soup.find_all('a', {'class' :'solution-link'})] " 43 | ] 44 | }, 45 | { 46 | "cell_type": "code", 47 | "execution_count": 3, 48 | "id": "05bea8ec-259c-4690-bea5-4181d37ba86e", 49 | "metadata": { 50 | "tags": [] 51 | }, 52 | "outputs": [], 53 | "source": [ 54 | "# scrape the names of solvers\n", 55 | "participants = []\n", 56 | "order = 1\n", 57 | "driver = webdriver.Firefox()\n", 58 | "for url in links:\n", 59 | " driver.get(url)\n", 60 | " time.sleep(5)\n", 61 | " htmlSource = driver.page_source\n", 62 | " soup = BeautifulSoup(htmlSource, 'html.parser')\n", 63 | " participants += [[str(url).split(\"/\")[-1],i.split(\" (\")[0],order] for i in soup.find_all('p', {'class' :'correct-submissions'})[0].stripped_strings]\n", 64 | " order +=1" 65 | ] 66 | }, 67 | { 68 | "cell_type": "code", 69 | "execution_count": 4, 70 | "id": "d7c4c1e2-e588-4328-8a2f-0319add0f92c", 71 | "metadata": { 72 | "tags": [] 73 | }, 74 | "outputs": [ 75 | { 76 | "data": { 77 | "text/html": [ 78 | "
\n", 79 | "\n", 92 | "\n", 93 | " \n", 94 | " \n", 95 | " \n", 96 | " \n", 97 | " \n", 98 | " \n", 99 | " \n", 100 | " \n", 101 | " \n", 102 | " \n", 103 | " \n", 104 | " \n", 105 | " \n", 106 | " \n", 107 | " \n", 108 | " \n", 109 | " \n", 110 | " \n", 111 | " \n", 112 | " \n", 113 | " \n", 114 | " \n", 115 | " \n", 116 | " \n", 117 | " \n", 118 | " \n", 119 | " \n", 120 | " \n", 121 | " \n", 122 | " \n", 123 | " \n", 124 | " \n", 125 | " \n", 126 | " \n", 127 | " \n", 128 | " \n", 129 | " \n", 130 | " \n", 131 | " \n", 132 | " \n", 133 | " \n", 134 | " \n", 135 | " \n", 136 | " \n", 137 | " \n", 138 | " \n", 139 | " \n", 140 | " \n", 141 | " \n", 142 | " \n", 143 | " \n", 144 | " \n", 145 | " \n", 146 | " \n", 147 | " \n", 148 | " \n", 149 | " \n", 150 | " \n", 151 | " \n", 152 | " \n", 153 | " \n", 154 | " \n", 155 | " \n", 156 | " \n", 157 | " \n", 158 | " \n", 159 | " \n", 160 | " \n", 161 | " \n", 162 | " \n", 163 | " \n", 164 | " \n", 165 | " \n", 166 | " \n", 167 | " \n", 168 | " \n", 169 | " \n", 170 | " \n", 171 | " \n", 172 | " \n", 173 | " \n", 174 | " \n", 175 | " \n", 176 | " \n", 177 | " \n", 178 | " \n", 179 | " \n", 180 | " \n", 181 | " \n", 182 | " \n", 183 | " \n", 184 | " \n", 185 | " \n", 186 | " \n", 187 | " \n", 188 | " \n", 189 | " \n", 190 | " \n", 191 | " \n", 192 | " \n", 193 | " \n", 194 | " \n", 195 | " \n", 196 | " \n", 197 | " \n", 198 | " \n", 199 | "
SolvedAttemptsScore
Name
Evan Semet43440.843137
Karl Mahlburg61680.813333
Senthil Rajasekaran67790.779070
Gareth Owen49590.742424
Aaditya Raghavan51620.739130
Lazar Ilic46560.730159
Danica Xiong24260.727273
Blaine Hill22240.709677
Calvin Pozderac70920.707071
Manuel Felizardo Roxo17180.680000
Miguel Barbosa Pereira30390.652174
Pedro Pereira16180.640000
Tiago França16180.640000
Josh Richman24310.631579
Anton 3 Terekhov23300.621622
\n", 200 | "
" 201 | ], 202 | "text/plain": [ 203 | " Solved Attempts Score\n", 204 | "Name \n", 205 | "Evan Semet 43 44 0.843137\n", 206 | "Karl Mahlburg 61 68 0.813333\n", 207 | "Senthil Rajasekaran 67 79 0.779070\n", 208 | "Gareth Owen 49 59 0.742424\n", 209 | "Aaditya Raghavan 51 62 0.739130\n", 210 | "Lazar Ilic 46 56 0.730159\n", 211 | "Danica Xiong 24 26 0.727273\n", 212 | "Blaine Hill 22 24 0.709677\n", 213 | "Calvin Pozderac 70 92 0.707071\n", 214 | "Manuel Felizardo Roxo 17 18 0.680000\n", 215 | "Miguel Barbosa Pereira 30 39 0.652174\n", 216 | "Pedro Pereira 16 18 0.640000\n", 217 | "Tiago França 16 18 0.640000\n", 218 | "Josh Richman 24 31 0.631579\n", 219 | "Anton 3 Terekhov 23 30 0.621622" 220 | ] 221 | }, 222 | "execution_count": 4, 223 | "metadata": {}, 224 | "output_type": "execute_result" 225 | } 226 | ], 227 | "source": [ 228 | "outputs = []\n", 229 | "for newcomer_offset in [7]:\n", 230 | " df = pd.DataFrame(participants,columns = ['puzzle','Name','Order'])\n", 231 | " df['Name']=df['Name'].str.lstrip(\"1234567890. \")\n", 232 | " df['Name'] = df['Name'].str.title()\n", 233 | " df2 = df[['Name','Order']].groupby('Name').agg({'Name':['count'],'Order': ['max']})\n", 234 | " df2.columns = [' '.join(col).strip() for col in df2.columns.values]\n", 235 | " df2.columns = ['Solved','Attempts']\n", 236 | " df2['Score']=df2['Solved']/(df2['Attempts']+ newcomer_offset)\n", 237 | " \n", 238 | "df2.sort_values(['Score','Solved'],ascending=False)[:15]" 239 | ] 240 | }, 241 | { 242 | "cell_type": "code", 243 | "execution_count": 5, 244 | "id": "0e58854e-e0ad-43ba-b979-950b04a815a4", 245 | "metadata": { 246 | "tags": [] 247 | }, 248 | "outputs": [ 249 | { 250 | "data": { 251 | "text/html": [ 252 | "
\n", 253 | "\n", 266 | "\n", 267 | " \n", 268 | " \n", 269 | " \n", 270 | " \n", 271 | " \n", 272 | " \n", 273 | " \n", 274 | " \n", 275 | " \n", 276 | " \n", 277 | " \n", 278 | " \n", 279 | " \n", 280 | " \n", 281 | " \n", 282 | " \n", 283 | " \n", 284 | " \n", 285 | " \n", 286 | " \n", 287 | " \n", 288 | " \n", 289 | " \n", 290 | " \n", 291 | " \n", 292 | " \n", 293 | " \n", 294 | " \n", 295 | " \n", 296 | " \n", 297 | " \n", 298 | " \n", 299 | " \n", 300 | " \n", 301 | " \n", 302 | " \n", 303 | " \n", 304 | " \n", 305 | " \n", 306 | " \n", 307 | " \n", 308 | " \n", 309 | " \n", 310 | " \n", 311 | " \n", 312 | " \n", 313 | " \n", 314 | " \n", 315 | " \n", 316 | " \n", 317 | " \n", 318 | " \n", 319 | " \n", 320 | " \n", 321 | " \n", 322 | " \n", 323 | " \n", 324 | " \n", 325 | " \n", 326 | " \n", 327 | " \n", 328 | " \n", 329 | " \n", 330 | " \n", 331 | " \n", 332 | " \n", 333 | " \n", 334 | " \n", 335 | " \n", 336 | " \n", 337 | " \n", 338 | " \n", 339 | " \n", 340 | " \n", 341 | " \n", 342 | " \n", 343 | " \n", 344 | " \n", 345 | " \n", 346 | " \n", 347 | " \n", 348 | " \n", 349 | " \n", 350 | " \n", 351 | " \n", 352 | " \n", 353 | " \n", 354 | " \n", 355 | " \n", 356 | " \n", 357 | " \n", 358 | " \n", 359 | " \n", 360 | " \n", 361 | " \n", 362 | " \n", 363 | " \n", 364 | " \n", 365 | " \n", 366 | " \n", 367 | " \n", 368 | " \n", 369 | " \n", 370 | " \n", 371 | " \n", 372 | " \n", 373 | "
SolvedAttemptsScore
Name
Calvin Pozderac70920.707071
Senthil Rajasekaran67790.779070
Karl Mahlburg61680.813333
Sean Egan54860.580645
Keith Schneider51830.566667
Aaditya Raghavan51620.739130
Gareth Owen49590.742424
Lazar Ilic46560.730159
Evan Semet43440.843137
Heidi Stockton401050.357143
Sébastien Geeraert38770.452381
Sanandan Swaminathan35550.564516
Michael Delyser31810.352273
Cubist31610.455882
Hutama301130.250000
\n", 374 | "
" 375 | ], 376 | "text/plain": [ 377 | " Solved Attempts Score\n", 378 | "Name \n", 379 | "Calvin Pozderac 70 92 0.707071\n", 380 | "Senthil Rajasekaran 67 79 0.779070\n", 381 | "Karl Mahlburg 61 68 0.813333\n", 382 | "Sean Egan 54 86 0.580645\n", 383 | "Keith Schneider 51 83 0.566667\n", 384 | "Aaditya Raghavan 51 62 0.739130\n", 385 | "Gareth Owen 49 59 0.742424\n", 386 | "Lazar Ilic 46 56 0.730159\n", 387 | "Evan Semet 43 44 0.843137\n", 388 | "Heidi Stockton 40 105 0.357143\n", 389 | "Sébastien Geeraert 38 77 0.452381\n", 390 | "Sanandan Swaminathan 35 55 0.564516\n", 391 | "Michael Delyser 31 81 0.352273\n", 392 | "Cubist 31 61 0.455882\n", 393 | "Hutama 30 113 0.250000" 394 | ] 395 | }, 396 | "execution_count": 5, 397 | "metadata": {}, 398 | "output_type": "execute_result" 399 | } 400 | ], 401 | "source": [ 402 | "df2.sort_values('Solved',ascending=False)[:15]" 403 | ] 404 | }, 405 | { 406 | "cell_type": "code", 407 | "execution_count": 6, 408 | "id": "5bec339e-fe11-48dd-afbb-680eaa40dac1", 409 | "metadata": { 410 | "tags": [] 411 | }, 412 | "outputs": [], 413 | "source": [ 414 | "import xlwings as xw\n", 415 | "sht = xw.Book().sheets[0]\n", 416 | "sht.range('a1').options(index=False).value = df" 417 | ] 418 | }, 419 | { 420 | "cell_type": "code", 421 | "execution_count": 7, 422 | "id": "11a66d54-e13d-4739-a93b-ff9a97f8628a", 423 | "metadata": {}, 424 | "outputs": [], 425 | "source": [ 426 | "#links" 427 | ] 428 | }, 429 | { 430 | "cell_type": "code", 431 | "execution_count": null, 432 | "id": "4a578406-cb51-4cff-8536-1ae9a39e238a", 433 | "metadata": {}, 434 | "outputs": [], 435 | "source": [] 436 | } 437 | ], 438 | "metadata": { 439 | "kernelspec": { 440 | "display_name": "Python 3 (ipykernel)", 441 | "language": "python", 442 | "name": "python3" 443 | }, 444 | "language_info": { 445 | "codemirror_mode": { 446 | "name": "ipython", 447 | "version": 3 448 | }, 449 | "file_extension": ".py", 450 | "mimetype": "text/x-python", 451 | "name": "python", 452 | "nbconvert_exporter": "python", 453 | "pygments_lexer": "ipython3", 454 | "version": "3.9.19" 455 | } 456 | }, 457 | "nbformat": 4, 458 | "nbformat_minor": 5 459 | } 460 | -------------------------------------------------------------------------------- /2014_06_QuestionMark.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 6, 6 | "metadata": { 7 | "id": "Oq1xibzMT1Wl" 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "import requests\n", 12 | "from bs4 import BeautifulSoup\n", 13 | "from IPython.display import Markdown, display\n", 14 | "from sympy.ntheory import primefactors\n", 15 | "import itertools\n", 16 | "import sympy\n", 17 | "import string" 18 | ] 19 | }, 20 | { 21 | "cell_type": "code", 22 | "execution_count": 2, 23 | "metadata": { 24 | "id": "ICTfvjZUT1Wr" 25 | }, 26 | "outputs": [ 27 | { 28 | "data": { 29 | "text/markdown": [ 30 | "### June 2014 : Puzzle\n", 31 | "\n", 32 | "223934\n", 33 | "1541\n", 34 | "14839\n", 35 | "259071087\n", 36 | "191558\n", 37 | "425859973\n", 38 | "276971\n", 39 | "137149\n", 40 | "27539479\n", 41 | "137149\n", 42 | "467797\n", 43 | "27539479\n", 44 | "137149\n", 45 | "467797\n", 46 | "745921\n", 47 | "277660180457\n", 48 | "989\n", 49 | "2078809\n", 50 | "18095\n", 51 | "?" 52 | ], 53 | "text/plain": [ 54 | "" 55 | ] 56 | }, 57 | "metadata": {}, 58 | "output_type": "display_data" 59 | } 60 | ], 61 | "source": [ 62 | "url='https://www.janestreet.com/puzzles/question-mark-index/'\n", 63 | "res = requests.get(url)\n", 64 | "soup = BeautifulSoup(res.content, 'html.parser')\n", 65 | "y =[text for text in soup.body.stripped_strings]\n", 66 | "#display([(i,j) for i,j in enumerate(y)])\n", 67 | "display(Markdown(\"### \"+y[8]+\"\\n\\n\"+str(\"\\n\".join(y[10:11]))))" 68 | ] 69 | }, 70 | { 71 | "cell_type": "code", 72 | "execution_count": 3, 73 | "metadata": { 74 | "id": "844RhDFpT1Wu" 75 | }, 76 | "outputs": [ 77 | { 78 | "name": "stdout", 79 | "output_type": "stream", 80 | "text": [ 81 | "['ahtw', 'is', 'eht', 'bemnru', 'aht', 'deilsy', 'otw', 'isx', 'ensv', 'isx', 'ein', 'ensv', 'isx', 'ein', 'ehnw', 'einrtw', 'in', 'hist', 'cdeo']\n" 82 | ] 83 | } 84 | ], 85 | "source": [ 86 | "data = [int(i) for i in y[10].split(\"\\n\")[:-1]]\n", 87 | "primes = list(sympy.primerange(0, 100))\n", 88 | "letters =[i for i in string.ascii_lowercase]\n", 89 | "print([\"\".join([letters[primes.index(j)] for j in primefactors(i)]) for i in data])" 90 | ] 91 | }, 92 | { 93 | "cell_type": "code", 94 | "execution_count": 4, 95 | "metadata": { 96 | "id": "844RhDFpT1Wu" 97 | }, 98 | "outputs": [ 99 | { 100 | "data": { 101 | "text/plain": [ 102 | "['f', 'i', 't', 'y']" 103 | ] 104 | }, 105 | "execution_count": 4, 106 | "metadata": {}, 107 | "output_type": "execute_result" 108 | } 109 | ], 110 | "source": [ 111 | "# \"what is the number that yields two six seven six nine seven six nine when written in this code\"\n", 112 | "\n", 113 | "[letters[primes.index(i)] for i in primefactors(26769769)]" 114 | ] 115 | }, 116 | { 117 | "cell_type": "code", 118 | "execution_count": 5, 119 | "metadata": {}, 120 | "outputs": [ 121 | { 122 | "data": { 123 | "text/markdown": [ 124 | "### June 2014 : Solution\n", 125 | "\n", 126 | "This month’s puzzle, as about 20 of you figured out, is a question written in\n", 127 | "code. Each of the given numbers is a composite number with prime factors less\n", 128 | "than or equal to 101. For each factorization, mapping the nth prime number to\n", 129 | "the nth letter of the alphabet and anagramming will give you each word. So, the\n", 130 | "question asks “What is the number that yields two six seven six nine seven six\n", 131 | "nine when written in this code?” Iterating again, 26769769 yields “fifty” (or\n", 132 | "“50” was accepted). Congrats to all solvers, especially Quinn Beightol, the\n", 133 | "randomly-chosen winner of this month’s Jane Street t-shirt!" 134 | ], 135 | "text/plain": [ 136 | "" 137 | ] 138 | }, 139 | "metadata": {}, 140 | "output_type": "display_data" 141 | } 142 | ], 143 | "source": [ 144 | "url='https://www.janestreet.com/puzzles/question-mark-solution/'\n", 145 | "res = requests.get(url)\n", 146 | "soup = BeautifulSoup(res.content, 'html.parser')\n", 147 | "y =[text for text in soup.body.stripped_strings]\n", 148 | "#display([(i,j) for i,j in enumerate(y)])\n", 149 | "display(Markdown(\"### \"+y[8]+\"\\n\\n\"+str(\"\\n\".join(y[10:11]))))" 150 | ] 151 | }, 152 | { 153 | "cell_type": "code", 154 | "execution_count": null, 155 | "metadata": {}, 156 | "outputs": [], 157 | "source": [] 158 | } 159 | ], 160 | "metadata": { 161 | "colab": { 162 | "include_colab_link": true, 163 | "name": "JaneSt-Feb18.ipynb", 164 | "provenance": [] 165 | }, 166 | "kernelspec": { 167 | "display_name": "Python 3", 168 | "language": "python", 169 | "name": "python3" 170 | }, 171 | "language_info": { 172 | "codemirror_mode": { 173 | "name": "ipython", 174 | "version": 3 175 | }, 176 | "file_extension": ".py", 177 | "mimetype": "text/x-python", 178 | "name": "python", 179 | "nbconvert_exporter": "python", 180 | "pygments_lexer": "ipython3", 181 | "version": "3.7.10" 182 | } 183 | }, 184 | "nbformat": 4, 185 | "nbformat_minor": 4 186 | } 187 | -------------------------------------------------------------------------------- /2014_11_WrongDivision.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "id": "c187f6c5", 7 | "metadata": {}, 8 | "outputs": [], 9 | "source": [ 10 | "import numpy as np\n", 11 | "import time\n", 12 | "import requests\n", 13 | "from bs4 import BeautifulSoup\n", 14 | "from z3 import *" 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": 2, 20 | "id": "fb433f2c", 21 | "metadata": {}, 22 | "outputs": [ 23 | { 24 | "name": "stdout", 25 | "output_type": "stream", 26 | "text": [ 27 | "Puzzle\n", 28 | "~~~~~~\n", 29 | "What We Do\n" 30 | ] 31 | } 32 | ], 33 | "source": [ 34 | "# More z3 magic\n", 35 | "\n", 36 | "url='https://www.janestreet.com/puzzles/wrong-division/'\n", 37 | "res = requests.get(url)\n", 38 | "soup = BeautifulSoup(res.content, 'html.parser')\n", 39 | "y =[text for text in soup.body.stripped_strings]\n", 40 | "print(\"Puzzle\")\n", 41 | "print(\"~~~~~~\")\n", 42 | "print(\" \".join(y[7:8]))" 43 | ] 44 | }, 45 | { 46 | "cell_type": "markdown", 47 | "id": "a4838c5f", 48 | "metadata": {}, 49 | "source": [ 50 | "" 51 | ] 52 | }, 53 | { 54 | "cell_type": "code", 55 | "execution_count": 3, 56 | "id": "8fcdd377", 57 | "metadata": {}, 58 | "outputs": [], 59 | "source": [ 60 | "def to_number(x):\n", 61 | " numb = 0\n", 62 | " for i in range(len(x)):\n", 63 | " numb += (10**i) *x[-(i+1)] \n", 64 | " return numb " 65 | ] 66 | }, 67 | { 68 | "cell_type": "code", 69 | "execution_count": 4, 70 | "id": "58ece50f", 71 | "metadata": {}, 72 | "outputs": [ 73 | { 74 | "name": "stdout", 75 | "output_type": "stream", 76 | "text": [ 77 | "Solved in 0.6403 seconds\n", 78 | "\n", 79 | "58,975,605 / 2,847 = 20,715\n", 80 | "\n", 81 | "Check : True\n", 82 | "row1: 5,694\n", 83 | "row2: 20,356\n", 84 | "row3: 19,929\n", 85 | "row4: 4,270\n", 86 | "row5: 2,847\n", 87 | "row6: 14,235\n" 88 | ] 89 | } 90 | ], 91 | "source": [ 92 | "start = time.time()\n", 93 | "\n", 94 | "#set up the variables and the numbers\n", 95 | "divisor = [Int(\"divisor_{}\".format(i)) for i in range(4)] \n", 96 | "dividend = [Int(\"dividend_{}\".format(i)) for i in range(8)]\n", 97 | "quotient = [Int(\"quotient_{}\".format(i)) for i in range(5)] \n", 98 | "row1 = [Int(\"row1_{}\".format(i)) for i in range(4)] \n", 99 | "row2 = [Int(\"row2_{}\".format(i)) for i in range(5)] \n", 100 | "row3 = [Int(\"row3_{}\".format(i)) for i in range(5)] \n", 101 | "row4 = [Int(\"row4_{}\".format(i)) for i in range(4)] \n", 102 | "row5 = [Int(\"row5_{}\".format(i)) for i in range(4)] \n", 103 | "row6 = [Int(\"row6_{}\".format(i)) for i in range(5)] \n", 104 | "\n", 105 | "s = Tactic(\"pqffd\").solver()\n", 106 | "\n", 107 | "#numbers in a range\n", 108 | "s += [And(0 <= divisor[i],divisor[i] <= 9) for i in range(4)]\n", 109 | "s += [And(0 <= dividend[i],dividend[i] <= 9) for i in range(8)]\n", 110 | "s += [And(0 <= quotient[i],quotient[i] <= 9) for i in range(5)]\n", 111 | "s += [And(0 <= row1[i],row1[i] <= 9) for i in range(4)]\n", 112 | "s += [And(0 <= row2[i],row2[i] <= 9) for i in range(5)]\n", 113 | "s += [And(0 <= row3[i],row3[i] <= 9) for i in range(5)]\n", 114 | "s += [And(0 <= row4[i],row4[i] <= 9) for i in range(4)]\n", 115 | "s += [And(0 <= row5[i],row5[i] <= 9) for i in range(4)]\n", 116 | "s += [And(0 <= row6[i],row6[i] <= 9) for i in range(5)]\n", 117 | "\n", 118 | "# the long division\n", 119 | "# ~~~~~~~~~~~~~~~~~\n", 120 | "divisor_num = sum([divisor[-(i+1)] * (10**i) for i in range(4)])\n", 121 | "dividend_num = sum([dividend[-(i+1)] * (10**i) for i in range(8)])\n", 122 | "quotient_num = sum([quotient[-(i+1)] * (10**i) for i in range(5)])\n", 123 | "\n", 124 | "s += dividend_num == divisor_num * quotient_num\n", 125 | "\n", 126 | "# line by line\n", 127 | "# ~~~~~~~~~~~~\n", 128 | "\n", 129 | "# the first quotient\n", 130 | "initial_num = sum([dividend[:4][-(i+1)] * (10**i) for i in range(4)])\n", 131 | "row1_num = sum([row1[-(i+1)] * (10**i) for i in range(4)])\n", 132 | "remainder1 = sum([row2[:3][-(i+1)] * (10**i) for i in range(3)])\n", 133 | "\n", 134 | "s += row1_num == divisor_num * quotient[0]\n", 135 | "s += initial_num - row1_num == remainder1\n", 136 | "\n", 137 | "# the second quotient\n", 138 | "\n", 139 | "s += quotient[1] == 0\n", 140 | "\n", 141 | "# the third quotient\n", 142 | "row2_num = sum([row2[-(i+1)] * (10**i) for i in range(5)])\n", 143 | "row3_num = sum([row3[-(i+1)] * (10**i) for i in range(5)])\n", 144 | "remainder2 = sum([row4[:3][-(i+1)] * (10**i) for i in range(3)])\n", 145 | "\n", 146 | "s += row2_num == remainder1*100+dividend[4]*10+dividend[5] \n", 147 | "s += row3_num == divisor_num * quotient[2]\n", 148 | "s += row2_num - row3_num == remainder2\n", 149 | "\n", 150 | "# the fourth quotient\n", 151 | "row4_num = sum([row4[-(i+1)] * (10**i) for i in range(4)])\n", 152 | "row5_num = sum([row5[-(i+1)] * (10**i) for i in range(4)])\n", 153 | "remainder3 = sum([row6[:4][-(i+1)] * (10**i) for i in range(4)])\n", 154 | "\n", 155 | "s += row4_num == remainder2*10+dividend[6] \n", 156 | "s += row5_num == divisor_num * quotient[3]\n", 157 | "s += row4_num - row5_num == remainder3\n", 158 | "\n", 159 | "# the fifth quotient\n", 160 | "row6_num = sum([row6[-(i+1)] * (10**i) for i in range(5)])\n", 161 | "s += row6_num == remainder3*10+dividend[7] \n", 162 | "s += row6_num == divisor_num * quotient[4]\n", 163 | "\n", 164 | "# Fixed numbers\n", 165 | "# ~~~~~~~~~~~~~\n", 166 | "s += Or(row1[1]==4,row1[1]==6)\n", 167 | "s += Or(row1[3]==4,row1[3]==6)\n", 168 | "\n", 169 | "s += row2[0]==2\n", 170 | "s += Or(row2[2]==1,row2[2]==3)\n", 171 | "s += Or(row2[4]==4,row2[4]==6)\n", 172 | "\n", 173 | "s += row3[0]==1\n", 174 | "s += Or(row3[2]==7,row3[2]==9)\n", 175 | "\n", 176 | "s += Or(row4[0]==4,row3[0]==6)\n", 177 | "s += Or(row4[2]==7,row3[2]==9)\n", 178 | "s += Or(row4[3]==0,row3[3]==2)\n", 179 | "\n", 180 | "s += Or(row5[0]==2,row5[0]==4)\n", 181 | "s += Or(row5[1]==6,row5[1]==8)\n", 182 | "\n", 183 | "s += Or(row6[0]==1,row6[0]==3)\n", 184 | "s += Or(row6[3]==1,row6[3]==3)\n", 185 | "\n", 186 | "#can't be a leading zero\n", 187 | "s += dividend[0]>0\n", 188 | "s += quotient[0]>0\n", 189 | "s += divisor[0]>0\n", 190 | "\n", 191 | "###################\n", 192 | "# Solve and Print #\n", 193 | "###################\n", 194 | "\n", 195 | "if s.check() == sat:\n", 196 | " m = s.model()\n", 197 | " \n", 198 | " divisor_result = to_number([ m.evaluate(divisor[i]).as_long() for i in range(4) ] )\n", 199 | " dividend_result = to_number([ m.evaluate(dividend[i]).as_long() for i in range(8) ] )\n", 200 | " quotient_result = to_number([ m.evaluate(quotient[i]).as_long() for i in range(5) ] )\n", 201 | " row1_result = to_number([ m.evaluate(row1[i]).as_long() for i in range(4) ] )\n", 202 | " row2_result = to_number([ m.evaluate(row2[i]).as_long() for i in range(5) ] )\n", 203 | " row3_result = to_number([ m.evaluate(row3[i]).as_long() for i in range(5) ] )\n", 204 | " row4_result = to_number([ m.evaluate(row4[i]).as_long() for i in range(4) ] )\n", 205 | " row5_result = to_number([ m.evaluate(row5[i]).as_long() for i in range(4) ] )\n", 206 | " row6_result = to_number([ m.evaluate(row6[i]).as_long() for i in range(5) ] )\n", 207 | " print(\"Solved in {:.4} seconds\".format(time.time()-start))\n", 208 | " print(\"\\n{:,.0f} / {:,.0f} = {:,.0f}\\n\".format(dividend_result,divisor_result,quotient_result))\n", 209 | " print(\"Check :\",dividend_result/ divisor_result == quotient_result) # sometimes gets stuck very close to the correct solution\n", 210 | " print(\"row1: {:,.0f}\".format(row1_result))\n", 211 | " print(\"row2: {:,.0f}\".format(row2_result))\n", 212 | " print(\"row3: {:,.0f}\".format(row3_result))\n", 213 | " print(\"row4: {:,.0f}\".format(row4_result))\n", 214 | " print(\"row5: {:,.0f}\".format(row5_result))\n", 215 | " print(\"row6: {:,.0f}\".format(row6_result))\n", 216 | " \n", 217 | "else:\n", 218 | " print(\"Failed\")" 219 | ] 220 | }, 221 | { 222 | "cell_type": "code", 223 | "execution_count": 5, 224 | "id": "e737de6e", 225 | "metadata": {}, 226 | "outputs": [ 227 | { 228 | "name": "stdout", 229 | "output_type": "stream", 230 | "text": [ 231 | "Solution\n", 232 | "~~~~~~~~\n", 233 | "What We Do\n" 234 | ] 235 | } 236 | ], 237 | "source": [ 238 | "url='https://www.janestreet.com/puzzles/solutions/november-2014-solution/'\n", 239 | "res = requests.get(url)\n", 240 | "soup = BeautifulSoup(res.content, 'html.parser')\n", 241 | "y =[text for text in soup.body.stripped_strings]\n", 242 | "print(\"Solution\")\n", 243 | "print(\"~~~~~~~~\")\n", 244 | "print(\" \".join(y[7:8]))" 245 | ] 246 | }, 247 | { 248 | "cell_type": "markdown", 249 | "id": "9c3b683c", 250 | "metadata": {}, 251 | "source": [ 252 | "" 253 | ] 254 | }, 255 | { 256 | "cell_type": "code", 257 | "execution_count": 6, 258 | "id": "cd3aa208", 259 | "metadata": {}, 260 | "outputs": [ 261 | { 262 | "data": { 263 | "text/plain": [ 264 | "(:max-memory 16.74\n", 265 | " :memory 3.52\n", 266 | " :num-allocs 127517982\n", 267 | " :par-progress 100.00\n", 268 | " :rlimit-count 2300857\n", 269 | " :sat-backjumps 7297\n", 270 | " :sat-conflicts 7297\n", 271 | " :sat-decisions 14704\n", 272 | " :sat-del-clause 44837\n", 273 | " :sat-elim-bool-vars-res 4554\n", 274 | " :sat-elim-clauses 15214\n", 275 | " :sat-elim-literals 10451\n", 276 | " :sat-minimized-lits 67811\n", 277 | " :sat-mk-clause-2ary 23709\n", 278 | " :sat-mk-clause-3ary 38405\n", 279 | " :sat-mk-clause-nary 29186\n", 280 | " :sat-mk-var 17522\n", 281 | " :sat-probing-assigned 2135\n", 282 | " :sat-propagations-2ary 1038695\n", 283 | " :sat-propagations-3ary 539567\n", 284 | " :sat-propagations-nary 427681\n", 285 | " :sat-restarts 508\n", 286 | " :sat-scc-elim-binary 42\n", 287 | " :sat-scc-elim-vars 1752\n", 288 | " :sat-subs-resolution 111\n", 289 | " :sat-subs-resolution-dyn 3544\n", 290 | " :sat-subsumed 3172\n", 291 | " :sat-units 6718\n", 292 | " :time 0.61)" 293 | ] 294 | }, 295 | "execution_count": 6, 296 | "metadata": {}, 297 | "output_type": "execute_result" 298 | } 299 | ], 300 | "source": [ 301 | "s.statistics()" 302 | ] 303 | }, 304 | { 305 | "cell_type": "code", 306 | "execution_count": null, 307 | "id": "4d0fa968", 308 | "metadata": {}, 309 | "outputs": [], 310 | "source": [] 311 | } 312 | ], 313 | "metadata": { 314 | "kernelspec": { 315 | "display_name": "Python 3", 316 | "language": "python", 317 | "name": "python3" 318 | }, 319 | "language_info": { 320 | "codemirror_mode": { 321 | "name": "ipython", 322 | "version": 3 323 | }, 324 | "file_extension": ".py", 325 | "mimetype": "text/x-python", 326 | "name": "python", 327 | "nbconvert_exporter": "python", 328 | "pygments_lexer": "ipython3", 329 | "version": "3.7.10" 330 | } 331 | }, 332 | "nbformat": 4, 333 | "nbformat_minor": 5 334 | } 335 | -------------------------------------------------------------------------------- /2015_05_TicTacOh.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "id": "46823cb5-38bc-44d0-8cd7-22b98fcb380b", 7 | "metadata": {}, 8 | "outputs": [], 9 | "source": [ 10 | "import numpy as np\n", 11 | "from copy import copy\n", 12 | "import random\n", 13 | "import seaborn as sns\n", 14 | "import matplotlib.pyplot as plt\n", 15 | "from fractions import Fraction\n", 16 | "import requests\n", 17 | "from bs4 import BeautifulSoup\n", 18 | "from IPython.display import Markdown, display,IFrame,HTML,Image\n" 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": 2, 24 | "id": "7cc3a8b7-12ee-4ac5-b074-ffe705030f4d", 25 | "metadata": {}, 26 | "outputs": [ 27 | { 28 | "data": { 29 | "text/markdown": [ 30 | "### Puzzle as published \n", 31 | "https://www.janestreet.com/puzzles/tic-tac-oh-index/" 32 | ], 33 | "text/plain": [ 34 | "" 35 | ] 36 | }, 37 | "metadata": {}, 38 | "output_type": "display_data" 39 | }, 40 | { 41 | "data": { 42 | "text/html": [ 43 | "
\n", 44 | "

You’re going to play Tic Tac Toe against a computer program, and you get to go\n", 45 | "first. The program is not very smart, and its strategy, which you are aware of,\n", 46 | "is to just mark a uniformly randomly chosen un-filled square on each turn. It\n", 47 | "would be really embarrassing not to beat this computer program, so in your eyes,\n", 48 | "a tie game is just as bad as a loss. What’s the maximum chance of winning you\n", 49 | "can give yourself?

\n", 50 | "
" 51 | ], 52 | "text/plain": [ 53 | "" 54 | ] 55 | }, 56 | "metadata": {}, 57 | "output_type": "display_data" 58 | } 59 | ], 60 | "source": [ 61 | "url='https://www.janestreet.com/puzzles/tic-tac-oh-index/'\n", 62 | "res = requests.get(url)\n", 63 | "soup = BeautifulSoup(res.content, 'html.parser')\n", 64 | "display(Markdown('### Puzzle as published \\n'+url))\n", 65 | "display(HTML(str(soup.find('div', {'class' :'inner-wrapper'}))))\n", 66 | "# Currently implemented minimax but best play against optimal != best play vs random\n", 67 | "\n", 68 | "#https://math.stackexchange.com/questions/4045893/if-two-computers-are-playing-tic-tac-toe-but-they-are-choosing-their-squares-ra" 69 | ] 70 | }, 71 | { 72 | "cell_type": "code", 73 | "execution_count": 3, 74 | "id": "c3ba0d74-e666-4b19-925a-70fb6f2f21d9", 75 | "metadata": {}, 76 | "outputs": [ 77 | { 78 | "data": { 79 | "image/png": "iVBORw0KGgoAAAANSUhEUgAAAH4AAAB7CAYAAACy7jQ7AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/YYfK9AAAACXBIWXMAAAsTAAALEwEAmpwYAAAFBUlEQVR4nO3dT0jTfxzH8ddXxJKgbx37ptd9E4k6tGF0ENzcMcZuwm6aHTp46N6tiA5lwaDVwD+dOmmXjiJi5BQPKkV48KCJhxoMHbWo8e5QP35E+4Pu8/1j79cDuvhd86VPdHPIR0tEQPq0BT2AgsHwSjG8UgyvFMMrxfBKtTe6aFkWf9Y7xkTEqnetYfjf/9nsmhZY1q+PI0ybgHDu+m9TPfxWrxTDK8XwSjG8UgyvFMMrxfBKMbxSDK9U01fu/kWLi4uYnJzExsYGvnz5AsdxkEgkMDo6Ctu2g57nC6vRy4yWZUkYX4ZsZdPTp0/x6NEjJBIJpFIp2LaNd+/e4fnz5zh16hSmp6dx7tw533eZZllWw9fqISJ1//26HB4ApJVNb9++Fdd15e7du39d297elmg0KplMxvddXvi9p25bVY/x+Xwetm3j9u3bf13r7u7GjRs3sLy8jLW1tQDW+UtN+B8/fmBlZQXXrl3DiRMnat5mYGAAALC0tOTntECoCV8qlVCpVHD+/Pm6t+nq6gIA7O3t+TUrMGrCS4ieeIWBmvBnz57FyZMnsbu7W/c2Hz9+BIBDP6s/jtSEb29vx5UrV/DmzRt8+/at5m3m5uYAAH19fX5OC4Sa8AAwMjKCUqmEhw8f/nVtZ2cH+Xwe0WgUly5dCmCdv9S9gJPNZvHkyRMMDg4ilUrh9OnTeP/+PZ49e4bOzk68ePECjuP4vsu0Zi/gqAsPAAsLC5iamsL6+jq+fv0Kx3EQj8dx8+ZNnDlzJrBdJjG8D8K4q1l4VY/x9D+GV4rhlWJ4pRheKYZXiuGVYnilGF4phleK4ZVq+nv1zU5WCEIYNwHh3VULv+KV4hk4BoRxF8/AoZoYXimGV4rhlWJ4pRheKYZXiuGVYnilPAs/NjaGWCyGT58+/fH2arWKdDqNZDKJSqXi1bs/NpsC0+i4DLRwvMfnz58lFovJrVu3/nh7LpcT13WlUCgc+j7R4pEjXmwyscsLaHIUiqdn4MzMzEgkEpHXr1+LiMjW1pZcvHhR7ty5c+QPJmybTO0yLdDwIiLDw8Ny9epVKRaLMjQ0JP39/XJwcHCk+zL1CTa5yeQukwIPv7u7K5cvX5Z4PC6RSETm5+ePfF+mPsEmN5ncZVKz8J4/q3ccB5lMBjs7O0gmk+jv7/f6XR7LTX7zPHy5XMarV69gWRY2NjZQLpe9fpfHcpPfPA//4MED7O/vI5fLoVgs1jyNwm9h3OS7Ro8DaPFxq1AoiOu6MjExISIi2WxWXNeV1dXVI90fDDyWmt5kapdpaPIY79nBCJVKBdevX4dt23j58iXa2trw/ft3pNNpVKtVzM7OoqOj41D32eqvOHmxycQuLwR2lu39+/elt7dXPnz48Mfb19bW5MKFC/L48eND3yda/MryYpOJXV5AED/Ora+vS09Pj4yPj9e8fu/ePent7ZXNzc1DfzBh29TqLq80C88zcAwI4y6egUM1MbxSDK8UwyvF8EoxvFIMrxTDK8XwSjG8UgyvFMMrxcOPDArrrlr4Fa8UDz8yIIy7ePgR1cTwSjG8UgyvFMMrxfBKMbxSDK8UwyvF8EoxvFIMrxTDK8XwSjG8UgyvFMMrxfBKMbxSDK8UwyvF8EoxvFIMrxTDK8XwSjG8UgyvFMMrxfBKMbxSDK8UwyvFM3AMCuuuWhr+hQr6d/FbvVIMrxTDK8XwSjG8Ugyv1E+68IsUdUFMhAAAAABJRU5ErkJggg==\n", 80 | "text/plain": [ 81 | "
" 82 | ] 83 | }, 84 | "metadata": { 85 | "needs_background": "light" 86 | }, 87 | "output_type": "display_data" 88 | } 89 | ], 90 | "source": [ 91 | "def to_board(moves):\n", 92 | " x_moves = moves[::2]\n", 93 | " o_moves = moves[1::2]\n", 94 | " annots = np.array(['X' if v in x_moves else 'O' if v in o_moves else '' for v in range(9)]).reshape((3,3))\n", 95 | " fig,ax = plt.subplots(1,1,figsize=(2,2))\n", 96 | " ax = sns.heatmap(np.zeros((3,3)),annot=annots,cbar=False,cmap=\"Greys\",fmt=\"\",linewidths=2,linecolor='k',annot_kws={\"fontsize\":16})\n", 97 | " ax.axis(\"off\")\n", 98 | "\n", 99 | " plt.show()\n", 100 | " \n", 101 | "to_board([4,1,3])" 102 | ] 103 | }, 104 | { 105 | "cell_type": "code", 106 | "execution_count": 4, 107 | "id": "5f615305-6be2-4fa1-91de-7f771b04e170", 108 | "metadata": {}, 109 | "outputs": [], 110 | "source": [ 111 | "def is_terminal(moves):\n", 112 | " p1_moves = moves[::2]\n", 113 | " p2_moves = moves[1::2]\n", 114 | " wins = [(0,1,2),(3,4,5),(6,7,8),(0,3,6),(1,4,7),(2,5,8),(0,4,8),(2,4,6)]\n", 115 | " for w in wins:\n", 116 | " if all([x in p1_moves for x in w]):\n", 117 | " return True,10\n", 118 | " elif all([x in p2_moves for x in w]):\n", 119 | " return True,-10\n", 120 | " if len(moves) == 9:\n", 121 | " return True,-10\n", 122 | " return False,0\n", 123 | "\n", 124 | "def produce_children(state):\n", 125 | " return [copy(state)+[i] for i in range(9) if i not in state]\n", 126 | "\n", 127 | "def maximize(state):\n", 128 | " terminal_status,reward = is_terminal(state)\n", 129 | " if terminal_status:\n", 130 | " return state,reward # No further state so return same state \n", 131 | " max_state, max_score = None,-np.Inf\n", 132 | " max_states = []\n", 133 | " children = produce_children(state)\n", 134 | " for child in children:\n", 135 | " _,score = minimize(child)\n", 136 | " if score > max_score:\n", 137 | " max_state,max_score = child,score\n", 138 | " max_states = [max_state]\n", 139 | " \n", 140 | " max_state = random.choice(max_states)\n", 141 | " \n", 142 | " return max_state,max_score\n", 143 | "\n", 144 | "def minimize(state): \n", 145 | " terminal_status,reward = is_terminal(state)\n", 146 | " if terminal_status:\n", 147 | " return state,reward # No further state so return same state \n", 148 | " min_state, min_score = None,np.Inf\n", 149 | " min_states = []\n", 150 | " children = produce_children(state)\n", 151 | " for child in children:\n", 152 | " _,score = maximize(child)\n", 153 | " if score < min_score:\n", 154 | " min_state,min_score = child,score\n", 155 | " min_states = [min_state]\n", 156 | " min_state = random.choice(min_states)\n", 157 | " \n", 158 | " return min_state,min_score" 159 | ] 160 | }, 161 | { 162 | "cell_type": "code", 163 | "execution_count": 5, 164 | "id": "1785893b-db0b-42bb-9d3f-c1c15e952a98", 165 | "metadata": {}, 166 | "outputs": [], 167 | "source": [ 168 | "def winner(moves):\n", 169 | " p1_moves = moves[::2]\n", 170 | " p2_moves = moves[1::2]\n", 171 | " wins = [(0,1,2),(3,4,5),(6,7,8),(0,3,6),(1,4,7),(2,5,8),(0,4,8),(2,4,6)]\n", 172 | " for w in wins:\n", 173 | " if all([x in p1_moves for x in w]):\n", 174 | " return 0\n", 175 | " elif all([x in p2_moves for x in w]):\n", 176 | " return 1\n", 177 | " if len(moves) == 9:\n", 178 | " return 2\n", 179 | " return -1\n", 180 | "\n", 181 | "def games(moves=[],optimal=False):\n", 182 | " score= [0,0,0]\n", 183 | " games = [[moves,1]]\n", 184 | " poss_games =[]\n", 185 | " n=0\n", 186 | " while len(games) >0:\n", 187 | " moves,prob = games.pop() \n", 188 | " if winner(moves) != -1:\n", 189 | " n +=1\n", 190 | " score[winner(moves)] += prob\n", 191 | " poss_games.append(moves)\n", 192 | " else:\n", 193 | " poss_moves = [i for i in range(9) if i not in moves]\n", 194 | " if len(moves) % 2 ==1 or optimal == False:\n", 195 | " next_prob = len(poss_moves)\n", 196 | " for i in poss_moves:\n", 197 | " games.append([copy(moves)+[i],prob*Fraction(1,next_prob)])\n", 198 | " else:\n", 199 | " games.append([copy(moves)+[maximize(moves)[0][-1]],prob]) \n", 200 | " return score,n," 201 | ] 202 | }, 203 | { 204 | "cell_type": "code", 205 | "execution_count": 6, 206 | "id": "81d7df4a-2edd-4046-82ae-d4abe2fc8528", 207 | "metadata": {}, 208 | "outputs": [ 209 | { 210 | "name": "stdout", 211 | "output_type": "stream", 212 | "text": [ 213 | "Start 0 results ([Fraction(191, 192), Fraction(1, 192), 0], 100)\n", 214 | "Start 1 results ([Fraction(353, 384), Fraction(13, 192), Fraction(5, 384)], 125)\n", 215 | "Start 4 results ([Fraction(185, 192), Fraction(13, 384), Fraction(1, 384)], 199)\n" 216 | ] 217 | } 218 | ], 219 | "source": [ 220 | "for i in [0,1,4]:\n", 221 | " print(\"Start {} results {}\".format(i,games([i],optimal=True)))" 222 | ] 223 | }, 224 | { 225 | "cell_type": "code", 226 | "execution_count": 7, 227 | "id": "a5598ed3-48f2-4784-bef5-00f2349271e2", 228 | "metadata": {}, 229 | "outputs": [ 230 | { 231 | "data": { 232 | "text/markdown": [ 233 | "### Solution as published \n", 234 | "https://www.janestreet.com/puzzles/tic-tac-oh-solution/" 235 | ], 236 | "text/plain": [ 237 | "" 238 | ] 239 | }, 240 | "metadata": {}, 241 | "output_type": "display_data" 242 | }, 243 | { 244 | "data": { 245 | "text/html": [ 246 | "
\n", 247 | "

The best way to face off against this computer program is to mark a corner. If\n", 248 | "the program does not select the middle square, you can always win. If it does\n", 249 | "(1/8), you select one of the 2 spots adjacent to your first mark. If the\n", 250 | "computer does not block you, you win. If it does block you (1/6), you block\n", 251 | "them, and have a chance to win. If the computer blocks you again (1/4), it has\n", 252 | "forced a tie, but otherwise you win. So, the best strategy yields a\n", 253 | "1-(1/8)(1/6)(1/4) = 191/192 chance of winning.

\n", 254 | "

Congratulations to those of you who submitted correct answers this month,\n", 255 | "especially Didrik Jonassen, this month’s winner of a Jane Street t-shirt!

\n", 256 | "

\n", 257 | "

\n", 258 | "
" 259 | ], 260 | "text/plain": [ 261 | "" 262 | ] 263 | }, 264 | "metadata": {}, 265 | "output_type": "display_data" 266 | } 267 | ], 268 | "source": [ 269 | "url='https://www.janestreet.com/puzzles/tic-tac-oh-solution/'\n", 270 | "res = requests.get(url)\n", 271 | "soup = BeautifulSoup(res.content, 'html.parser')\n", 272 | "display(Markdown('### Solution as published \\n'+url))\n", 273 | "display(HTML(str(soup.find('div', {'class' :'inner-wrapper'}))))" 274 | ] 275 | }, 276 | { 277 | "cell_type": "code", 278 | "execution_count": 8, 279 | "id": "fb7f1b14-c202-47a2-9c52-3f46862ae345", 280 | "metadata": {}, 281 | "outputs": [ 282 | { 283 | "data": { 284 | "text/plain": [ 285 | "([Fraction(737, 1260), Fraction(121, 420), Fraction(8, 63)], 255168)" 286 | ] 287 | }, 288 | "execution_count": 8, 289 | "metadata": {}, 290 | "output_type": "execute_result" 291 | } 292 | ], 293 | "source": [ 294 | "games([])" 295 | ] 296 | }, 297 | { 298 | "cell_type": "code", 299 | "execution_count": 9, 300 | "id": "711bea91-9d00-4d17-a967-eb4a1e2e3806", 301 | "metadata": {}, 302 | "outputs": [ 303 | { 304 | "data": { 305 | "text/plain": [ 306 | "([Fraction(191, 192), Fraction(1, 192), 0], 100)" 307 | ] 308 | }, 309 | "execution_count": 9, 310 | "metadata": {}, 311 | "output_type": "execute_result" 312 | } 313 | ], 314 | "source": [ 315 | "games([],optimal=True)" 316 | ] 317 | }, 318 | { 319 | "cell_type": "code", 320 | "execution_count": 10, 321 | "id": "f8bba89b-041e-4837-a280-84513072791f", 322 | "metadata": {}, 323 | "outputs": [ 324 | { 325 | "data": { 326 | "text/plain": [ 327 | "0.9947916666666666" 328 | ] 329 | }, 330 | "execution_count": 10, 331 | "metadata": {}, 332 | "output_type": "execute_result" 333 | } 334 | ], 335 | "source": [ 336 | "191/192" 337 | ] 338 | }, 339 | { 340 | "cell_type": "code", 341 | "execution_count": null, 342 | "id": "fd3ae881-a3aa-460f-be31-9eef6ceeb4fe", 343 | "metadata": {}, 344 | "outputs": [], 345 | "source": [] 346 | } 347 | ], 348 | "metadata": { 349 | "kernelspec": { 350 | "display_name": "Python 3 (ipykernel)", 351 | "language": "python", 352 | "name": "python3" 353 | }, 354 | "language_info": { 355 | "codemirror_mode": { 356 | "name": "ipython", 357 | "version": 3 358 | }, 359 | "file_extension": ".py", 360 | "mimetype": "text/x-python", 361 | "name": "python", 362 | "nbconvert_exporter": "python", 363 | "pygments_lexer": "ipython3", 364 | "version": "3.7.12" 365 | } 366 | }, 367 | "nbformat": 4, 368 | "nbformat_minor": 5 369 | } 370 | -------------------------------------------------------------------------------- /2015_06_Polymath.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "id": "5c3093f2", 7 | "metadata": {}, 8 | "outputs": [], 9 | "source": [ 10 | "import numpy as np\n", 11 | "import pandas as pd\n", 12 | "import time\n", 13 | "import seaborn as sns\n", 14 | "import matplotlib.pyplot as plt\n", 15 | "import requests\n", 16 | "from bs4 import BeautifulSoup\n", 17 | "import itertools\n", 18 | "from copy import deepcopy as dcopy,copy\n", 19 | "from skimage.morphology import label\n", 20 | "from scipy.ndimage import measurements\n", 21 | "import sys\n", 22 | "import numba as nb" 23 | ] 24 | }, 25 | { 26 | "cell_type": "code", 27 | "execution_count": 6, 28 | "id": "1744190f", 29 | "metadata": {}, 30 | "outputs": [], 31 | "source": [ 32 | "url='https://www.janestreet.com/puzzles/polymath/'\n", 33 | "res = requests.get(url)\n", 34 | "soup = BeautifulSoup(res.content, 'html.parser')\n", 35 | "y =[text for text in soup.body.stripped_strings]\n", 36 | "print(\"Puzzle\")\n", 37 | "print(\"~~~~~~\")\n", 38 | "print(\" \".join(y[8:24]))" 39 | ] 40 | }, 41 | { 42 | "cell_type": "markdown", 43 | "id": "b99b59e6", 44 | "metadata": {}, 45 | "source": [ 46 | "\n", 47 | "\n" 48 | ] 49 | }, 50 | { 51 | "cell_type": "code", 52 | "execution_count": 16, 53 | "id": "4347294b", 54 | "metadata": {}, 55 | "outputs": [ 56 | { 57 | "data": { 58 | "image/png": "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\n", 59 | "text/plain": [ 60 | "
" 61 | ] 62 | }, 63 | "metadata": { 64 | "needs_background": "light" 65 | }, 66 | "output_type": "display_data" 67 | } 68 | ], 69 | "source": [ 70 | "fixed = [[6,7,5,2,6,4,2,1,4,4],\n", 71 | " [5,4,6,7,7,2,4,2,6,1],\n", 72 | " [5,1,7,3,4,5,5,4,7,4],\n", 73 | " [2,7,6,5,1,1,2,4,6,1],\n", 74 | " [1,5,3,5,3,5,3,4,5,1],\n", 75 | " [6,1,2,3,4,4,5,7,2,3],\n", 76 | " [1,6,5,3,3,6,5,1,1,7],\n", 77 | " [2,1,1,2,1,7,1,3,3,3],\n", 78 | " [7,4,4,6,3,4,1,1,6,2],\n", 79 | " [4,6,5,6,2,3,7,2,3,6]]\n", 80 | " \n", 81 | "def grid_print(fixed):\n", 82 | " fig,ax = plt.subplots(1,1,figsize=(4,4))\n", 83 | " ax = sns.heatmap(fixed,annot=fixed,cbar=False,cmap=\"tab20_r\",fmt=\"\",linewidths=1,annot_kws={\"size\":14})\n", 84 | " ax.axis(\"off\")\n", 85 | " plt.tight_layout()\n", 86 | " \n", 87 | "grid_print(fixed)" 88 | ] 89 | }, 90 | { 91 | "cell_type": "code", 92 | "execution_count": 12, 93 | "id": "11673577", 94 | "metadata": {}, 95 | "outputs": [ 96 | { 97 | "name": "stdout", 98 | "output_type": "stream", 99 | "text": [ 100 | "Puzzle\n", 101 | "~~~~~~\n", 102 | "As most solvers realized, it was better to try and use a large n with fewer n-ominoes than a smaller n with many n-ominoes.   The top score we received from a handful of solvers was 20,160.  Interestingly, there are (at least) 2 different ways of getting to this score — one way is presented here, see if you can find the other! Congratulations to Wouter Bosma, one of the 20,160 submitters, and this month’s randomly-chosen winner of a Jane Street t-shirt!\n" 103 | ] 104 | } 105 | ], 106 | "source": [ 107 | "url='https://www.janestreet.com/puzzles/solutions/june-2015-solution/'\n", 108 | "res = requests.get(url)\n", 109 | "soup = BeautifulSoup(res.content, 'html.parser')\n", 110 | "y =[text for text in soup.body.stripped_strings]\n", 111 | "print(\"Puzzle\")\n", 112 | "print(\"~~~~~~\")\n", 113 | "print(\" \".join(y[7:9]))" 114 | ] 115 | }, 116 | { 117 | "cell_type": "markdown", 118 | "id": "5a36078f", 119 | "metadata": {}, 120 | "source": [ 121 | "\n", 122 | "\n" 123 | ] 124 | }, 125 | { 126 | "cell_type": "code", 127 | "execution_count": 15, 128 | "id": "756aa42b", 129 | "metadata": {}, 130 | "outputs": [ 131 | { 132 | "data": { 133 | "text/plain": [ 134 | "20160" 135 | ] 136 | }, 137 | "execution_count": 15, 138 | "metadata": {}, 139 | "output_type": "execute_result" 140 | } 141 | ], 142 | "source": [ 143 | "import math\n", 144 | "\n", 145 | "math.factorial(7)*4" 146 | ] 147 | }, 148 | { 149 | "cell_type": "code", 150 | "execution_count": null, 151 | "id": "76f39408", 152 | "metadata": {}, 153 | "outputs": [], 154 | "source": [] 155 | } 156 | ], 157 | "metadata": { 158 | "kernelspec": { 159 | "display_name": "Python 3", 160 | "language": "python", 161 | "name": "python3" 162 | }, 163 | "language_info": { 164 | "codemirror_mode": { 165 | "name": "ipython", 166 | "version": 3 167 | }, 168 | "file_extension": ".py", 169 | "mimetype": "text/x-python", 170 | "name": "python", 171 | "nbconvert_exporter": "python", 172 | "pygments_lexer": "ipython3", 173 | "version": "3.7.10" 174 | } 175 | }, 176 | "nbformat": 4, 177 | "nbformat_minor": 5 178 | } 179 | -------------------------------------------------------------------------------- /2015_09_ThinkOfTheChildren.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": { 7 | "id": "Oq1xibzMT1Wl" 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "import numpy as np\n", 12 | "import time\n", 13 | "import requests\n", 14 | "from bs4 import BeautifulSoup\n", 15 | "import itertools" 16 | ] 17 | }, 18 | { 19 | "cell_type": "code", 20 | "execution_count": 2, 21 | "metadata": { 22 | "id": "ICTfvjZUT1Wr" 23 | }, 24 | "outputs": [ 25 | { 26 | "name": "stdout", 27 | "output_type": "stream", 28 | "text": [ 29 | "Puzzle\n", 30 | "~~~~~~\n", 31 | "Andy: How many children 18 or younger do you have? Bob: 3 Andy: How old are they? Bob: Well, the product of their ages is 72, and the sum of their ages is the same as my address. Andy: I still can’t determine their ages. Bob: Oh, I should mention that my oldest child is older than either of my other 2 children. Andy: Ok, now I know how old your children are. Instead of 72, what is the next largest number that Bob could have said, such that the rest of the conversation could have proceeded the same way?\n" 32 | ] 33 | } 34 | ], 35 | "source": [ 36 | "url='https://www.janestreet.com/puzzles/think-of-the-children/'\n", 37 | "res = requests.get(url)\n", 38 | "soup = BeautifulSoup(res.content, 'html.parser')\n", 39 | "y =[text for text in soup.body.stripped_strings]\n", 40 | "print(\"Puzzle\")\n", 41 | "print(\"~~~~~~\")\n", 42 | "print(\" \".join(y[8:16]))" 43 | ] 44 | }, 45 | { 46 | "cell_type": "code", 47 | "execution_count": 18, 48 | "metadata": {}, 49 | "outputs": [], 50 | "source": [ 51 | "def get_dup_facts(number):\n", 52 | " age = 18\n", 53 | " facts = [[i,j,k] for i in range(1,age+1) for j in range(i,age+1) for k in range(j,age+1) if i*j*k ==number]\n", 54 | " sum_facts = [sum(i) for i in facts]\n", 55 | " duplicates = ([x for n, x in enumerate(sum_facts) if x in sum_facts[:n]])\n", 56 | " return [x for x in facts if np.sum(x) in duplicates]" 57 | ] 58 | }, 59 | { 60 | "cell_type": "code", 61 | "execution_count": 22, 62 | "metadata": {}, 63 | "outputs": [ 64 | { 65 | "name": "stdout", 66 | "output_type": "stream", 67 | "text": [ 68 | "The factors of 72 with the same sums are :\n", 69 | "[[2, 6, 6], [3, 3, 8]]\n", 70 | "Look for the next one with 2 different factors summing to the same number with duplicates\n", 71 | "\n", 72 | "Searching ...\n", 73 | "Solved in 0.0299 seconds\n", 74 | "\n", 75 | "The next solution is 288 [[2, 12, 12], [4, 4, 18]]\n" 76 | ] 77 | } 78 | ], 79 | "source": [ 80 | "start=time.time()\n", 81 | "\n", 82 | "print(\"The factors of 72 with the same sums are :\")\n", 83 | "print(get_dup_facts(72))\n", 84 | "\n", 85 | "print(\"Look for the next one with 2 different factors summing to the same number with duplicates\")\n", 86 | "print(\"\\nSearching ...\")\n", 87 | "for n in range(73,1000):\n", 88 | " dupfacts = get_dup_facts(n)\n", 89 | " if len(dupfacts) ==2:\n", 90 | " if ((dupfacts[0][1] == dupfacts[0][2]) & (dupfacts[1][0] == dupfacts[1][1])) | ((dupfacts[0][0] == dupfacts[0][1]) & (dupfacts[1][1] == dupfacts[1][2])):\n", 91 | " print(\"Solved in {:.4f} seconds\\n\".format(time.time()-start))\n", 92 | " print(\"The next solution is\",n,dupfacts)\n", 93 | " break" 94 | ] 95 | }, 96 | { 97 | "cell_type": "code", 98 | "execution_count": 30, 99 | "metadata": {}, 100 | "outputs": [ 101 | { 102 | "name": "stdout", 103 | "output_type": "stream", 104 | "text": [ 105 | "Solution\n", 106 | "~~~~~~~~\n", 107 | "In order for the conversation to proceed as it did, the product of the ages of Bob’s children needs to be able to be factored in 2 different ways, one of which has the largest 2 factors being the same as each other, and which have the same sum of factors.  For example, with 72, Andy would have been unsure whether Bob’s children were aged (8,3,3) or (6,6,2) even knowing his address was 14, until having his final question answered. The next largest number that satisfies these constraints is 288 .  Some submitters interpreted “next largest” to mean the largest number smaller than 72, not the smallest number larger than 72.  Since this was a bit vague, we accepted 36 as correct as well. One of the most popular incorrect answers was 225, with the thought that Bob’s children could have been (25,3,3) or (15,15,1).  However, 25 is too old to be a child! Congratulations to all of the correct responders, especially Alice Scarpa, this month’s randomly-chosen winner of a Jane Street t-shirt!\n" 108 | ] 109 | } 110 | ], 111 | "source": [ 112 | "url='https://www.janestreet.com/puzzles/solutions/september-2015-solution/'\n", 113 | "res = requests.get(url)\n", 114 | "soup = BeautifulSoup(res.content, 'html.parser')\n", 115 | "x =[text for text in soup.body.stripped_strings]\n", 116 | "\n", 117 | "print(\"Solution\")\n", 118 | "print(\"~~~~~~~~\")\n", 119 | "print(\" \".join(x[7:13]))" 120 | ] 121 | }, 122 | { 123 | "cell_type": "code", 124 | "execution_count": null, 125 | "metadata": {}, 126 | "outputs": [], 127 | "source": [] 128 | } 129 | ], 130 | "metadata": { 131 | "colab": { 132 | "include_colab_link": true, 133 | "name": "JaneSt-Feb18.ipynb", 134 | "provenance": [] 135 | }, 136 | "kernelspec": { 137 | "display_name": "Python 3", 138 | "language": "python", 139 | "name": "python3" 140 | }, 141 | "language_info": { 142 | "codemirror_mode": { 143 | "name": "ipython", 144 | "version": 3 145 | }, 146 | "file_extension": ".py", 147 | "mimetype": "text/x-python", 148 | "name": "python", 149 | "nbconvert_exporter": "python", 150 | "pygments_lexer": "ipython3", 151 | "version": "3.7.10" 152 | } 153 | }, 154 | "nbformat": 4, 155 | "nbformat_minor": 4 156 | } 157 | -------------------------------------------------------------------------------- /2015_12_ProfessorRando.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 4, 6 | "metadata": { 7 | "id": "Oq1xibzMT1Wl" 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "import numpy as np\n", 12 | "import requests\n", 13 | "from bs4 import BeautifulSoup\n" 14 | ] 15 | }, 16 | { 17 | "cell_type": "code", 18 | "execution_count": 9, 19 | "metadata": { 20 | "id": "ICTfvjZUT1Wr" 21 | }, 22 | "outputs": [ 23 | { 24 | "name": "stdout", 25 | "output_type": "stream", 26 | "text": [ 27 | "Puzzle\n", 28 | "~~~~~~\n", 29 | "Puzzle Archive Professor Rando Professor Rando has 4 grad students, Daphne, Max, Mindy, and Sam.  The professor proposes the following game: he generates two random integers independently and uniformly from 1 to 5 (inclusive), and then tells each of his students a different fact about the two numbers.  He tells: Daphne the absolute difference of the integers, Max the maximum of the integers, Mindy the minimum of the integers, and Sam the sum of the integers. Then, each day until the game ends, he congregates his students and asks them, in alphabetical order, for the identity of the two integers.  Each student has only one chance to answer each day, when she or he is called upon.  Each student answers ONLY when the answer is definitively known to him or her, and otherwise gives no answer that day.  All of the students know this, and there is no collusion.  Once a student gives an answer (which will be correct), that student wins and the game ends. How likely is each student to win?\n" 30 | ] 31 | } 32 | ], 33 | "source": [ 34 | "url='https://www.janestreet.com/puzzles/professor-rando/'\n", 35 | "res = requests.get(url)\n", 36 | "soup = BeautifulSoup(res.content, 'html.parser')\n", 37 | "y =[text for text in soup.body.stripped_strings]\n", 38 | "print(\"Puzzle\")\n", 39 | "print(\"~~~~~~\")\n", 40 | "print(\" \".join(y[5:14]))" 41 | ] 42 | }, 43 | { 44 | "cell_type": "code", 45 | "execution_count": 10, 46 | "metadata": { 47 | "id": "844RhDFpT1Wu" 48 | }, 49 | "outputs": [ 50 | { 51 | "name": "stdout", 52 | "output_type": "stream", 53 | "text": [ 54 | "The winner is Sam\n", 55 | "\n", 56 | "Daphne has a 5/25 chance\n", 57 | "Max has a 6/25 chance\n", 58 | "Mindy has a 6/25 chance\n", 59 | "Sam has a 8/25 chance\n" 60 | ] 61 | } 62 | ], 63 | "source": [ 64 | "\n", 65 | "players = [\"Daphne\",\"Max\",\"Mindy\",\"Sam\"]\n", 66 | "\n", 67 | "numbers = 5\n", 68 | "outcomes =[[i,j,[abs(j-i),max(i,j),min(i,j),i+j]] for i in range(1,numbers+1) for j in range(i,numbers+1)]\n", 69 | "\n", 70 | "player = 0 \n", 71 | "winner = [0]*4\n", 72 | "\n", 73 | "while len(outcomes) >0:\n", 74 | " #determine the posible values and count them for each player \n", 75 | " round = [x[2][player] for x in outcomes]\n", 76 | " options = {x:round.count(x) for x in round}\n", 77 | "\n", 78 | " \n", 79 | " #if there is a single option eliminate it and mark the player as the winner (noting there are 2 ways to get double numbers)\n", 80 | " for key,value in options.items():\n", 81 | " if value ==1:\n", 82 | " score = 1+np.sum([(x[0]!=x[1]) for x in outcomes if x[2][player] == key])\n", 83 | " outcomes = [x for x in outcomes if x[2][player] != key]\n", 84 | " winner[player] += score\n", 85 | " player = (player +1) % 4\n", 86 | " \n", 87 | "\n", 88 | "print(\"The winner is {}\\n\".format(players[np.argmax(winner)]))\n", 89 | "\n", 90 | "for i in range(len(players)):\n", 91 | " print(\"{} has a {}/{} chance\".format(players[i],winner[i],numbers**2))" 92 | ] 93 | }, 94 | { 95 | "cell_type": "code", 96 | "execution_count": 14, 97 | "metadata": {}, 98 | "outputs": [ 99 | { 100 | "name": "stdout", 101 | "output_type": "stream", 102 | "text": [ 103 | "Solution\n", 104 | "~~~~~~~~\n", 105 | "The chances that the 4 students win are: Daphne: 1/5 Max: 6/25 Mindy: 6/25 Sam: 8/25 Someone always wins, although it might take more than one day. Note that Professor Rando only draws his numbers once, not each day, and also note that Professor Rando is twice as likely to draw any given set of 2 different numbers than he is to draw any given set of 2 identical numbers. We’ll leave out a more thorough explanation for now, since January’s puzzle also relates to Professor Rando. Congratulations to everyone who submitted a correct solution this month, especially Neil Alberg, this month’s randomly-selected winner of a Jane Street t-shirt!\n" 106 | ] 107 | } 108 | ], 109 | "source": [ 110 | "url='https://www.janestreet.com/puzzles/solutions/december-2015-solution/'\n", 111 | "res = requests.get(url)\n", 112 | "soup = BeautifulSoup(res.content, 'html.parser')\n", 113 | "x =[text for text in soup.body.stripped_strings]\n", 114 | "\n", 115 | "print(\"Solution\")\n", 116 | "print(\"~~~~~~~~\")\n", 117 | "print(\" \".join(x[7:14]))" 118 | ] 119 | }, 120 | { 121 | "cell_type": "code", 122 | "execution_count": null, 123 | "metadata": {}, 124 | "outputs": [], 125 | "source": [] 126 | } 127 | ], 128 | "metadata": { 129 | "colab": { 130 | "include_colab_link": true, 131 | "name": "JaneSt-Feb18.ipynb", 132 | "provenance": [] 133 | }, 134 | "kernelspec": { 135 | "display_name": "Python 3", 136 | "language": "python", 137 | "name": "python3" 138 | }, 139 | "language_info": { 140 | "codemirror_mode": { 141 | "name": "ipython", 142 | "version": 3 143 | }, 144 | "file_extension": ".py", 145 | "mimetype": "text/x-python", 146 | "name": "python", 147 | "nbconvert_exporter": "python", 148 | "pygments_lexer": "ipython3", 149 | "version": "3.7.7" 150 | } 151 | }, 152 | "nbformat": 4, 153 | "nbformat_minor": 4 154 | } 155 | -------------------------------------------------------------------------------- /2016_01_ProfessorRandoRedux.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 12, 6 | "metadata": { 7 | "id": "Oq1xibzMT1Wl" 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "import numpy as np\n", 12 | "import requests\n", 13 | "from bs4 import BeautifulSoup\n", 14 | "from IPython.display import Markdown, display" 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": 13, 20 | "metadata": { 21 | "id": "ICTfvjZUT1Wr" 22 | }, 23 | "outputs": [ 24 | { 25 | "data": { 26 | "text/markdown": [ 27 | "**Professor Rando Redux**\n", 28 | "\n", 29 | "You may have noticed from December’s puzzle that if Professor Rando selects his two integers uniformly from the range 1 to N, where N > 5, there is some chance that the game never ends.\n", 30 | "\n", 31 | "But, the professor has recently gotten a very bright new pupil, Tim, who wants to be included in the game. Rando decides the game will work similarly as before:\n", 32 | "\n", 33 | "For some positive integer N, he generates two random integers independently and uniformly from 1 to N (inclusive), and then tells each of his students a different fact about the two numbers. He tells:\n", 34 | "\n", 35 | "•\tDaphne the absolute difference of the integers,\n", 36 | "\n", 37 | "•\tMax the maximum of the integers,\n", 38 | "\n", 39 | "•\tMindy the minimum of the integers,\n", 40 | "\n", 41 | "•\tSam the sum of the integers, and\n", 42 | "\n", 43 | "•\tTim the product of the integers.\n", 44 | "\n", 45 | "Then, each day until the game ends, he congregates his students and asks them, in alphabetical order, for the identity of the two integers. Each student has only one chance to answer each day, when she or he is called upon. Each student answers ONLY when the answer is definitively known to him or her, and otherwise gives no answer that day. All of the students know this, and there is no collusion. Once a student gives an answer (which will be correct), that student wins and the game ends.\n", 46 | "\n", 47 | "Will this game always eventually end? If so, submit “yes”. If not, submit the smallest N such that the game might not necessarily ever end." 48 | ], 49 | "text/plain": [ 50 | "" 51 | ] 52 | }, 53 | "metadata": {}, 54 | "output_type": "display_data" 55 | } 56 | ], 57 | "source": [ 58 | "url='https://www.janestreet.com/puzzles/professor-rando-redux/'\n", 59 | "res = requests.get(url)\n", 60 | "soup = BeautifulSoup(res.content, 'html.parser')\n", 61 | "y =[text for text in soup.body.stripped_strings]\n", 62 | "\n", 63 | "display(Markdown(\"**\"+y[6]+\"**\\n\\n\"+str(\"\\n\\n\".join(y[7:17]))))" 64 | ] 65 | }, 66 | { 67 | "cell_type": "code", 68 | "execution_count": 77, 69 | "metadata": { 70 | "id": "844RhDFpT1Wu" 71 | }, 72 | "outputs": [ 73 | { 74 | "name": "stdout", 75 | "output_type": "stream", 76 | "text": [ 77 | "For 2 numbers the winner is Daphne. [Daphne:2/4 Max:2/4 Mindy:0/4 Sam:0/4 Tim:0/4] Total = 4\n", 78 | "For 3 numbers the winner is Mindy. [Daphne:2/9 Max:1/9 Mindy:3/9 Sam:3/9 Tim:0/9] Total = 9\n", 79 | "For 4 numbers the winner is Sam. [Daphne:2/16 Max:1/16 Mindy:1/16 Sam:6/16 Tim:6/16] Total = 16\n", 80 | "For 5 numbers the winner is Tim. [Daphne:5/25 Max:1/25 Mindy:1/25 Sam:4/25 Tim:14/25] Total = 25\n", 81 | "For 6 numbers the winner is Tim. [Daphne:9/36 Max:1/36 Mindy:1/36 Sam:4/36 Tim:21/36] Total = 36\n", 82 | "For 7 numbers the winner is Tim. [Daphne:9/49 Max:5/49 Mindy:1/49 Sam:4/49 Tim:30/49] Total = 49\n", 83 | "For 8 numbers the winner is Tim. [Daphne:10/64 Max:8/64 Mindy:4/64 Sam:4/64 Tim:38/64] Total = 64\n", 84 | "For 9 numbers the winner is Tim. [Daphne:10/81 Max:4/81 Mindy:2/81 Sam:13/81 Tim:52/81] Total = 81\n", 85 | "For 10 numbers the winner is Tim. [Daphne:9/100 Max:12/100 Mindy:8/100 Sam:13/100 Tim:58/100] Total = 100\n", 86 | "For 11 numbers the winner is Tim. [Daphne:11/121 Max:10/121 Mindy:8/121 Sam:13/121 Tim:79/121] Total = 121\n", 87 | "For 12 numbers the winner is Tim. [Daphne:18/144 Max:13/144 Mindy:4/144 Sam:15/144 Tim:94/144] Total = 144\n", 88 | "For 13 numbers the winner is Tim. [Daphne:20/169 Max:13/169 Mindy:4/169 Sam:15/169 Tim:117/169] Total = 169\n", 89 | "For 14 numbers the winner is Tim. [Daphne:19/196 Max:11/196 Mindy:7/196 Sam:23/196 Tim:136/196] Total = 196\n", 90 | "===============================\n", 91 | "For 15 numbers there is no winner\n", 92 | "===============================\n" 93 | ] 94 | } 95 | ], 96 | "source": [ 97 | "# goes into a loop when numbers =15\n", 98 | "\n", 99 | "players = [\"Daphne\",\"Max\",\"Mindy\",\"Sam\",\"Tim\"]\n", 100 | "\n", 101 | "for numbers in range(2,16):\n", 102 | "\n", 103 | " outcomes =[[i,j,[abs(j-i),max(i,j),min(i,j),i+j,i*j]] for i in range(1,numbers+1) for j in range(i,numbers+1)]\n", 104 | "\n", 105 | " player = 0 \n", 106 | " winner = [0]*5\n", 107 | " nochange =5\n", 108 | " \n", 109 | " while len(outcomes)>0:\n", 110 | " #determine the posible values and count them for each player \n", 111 | " round = [x[2][player] for x in outcomes]\n", 112 | " options = {x:round.count(x) for x in round}\n", 113 | "\n", 114 | " \n", 115 | " #if there is a single option eliminate it and mark the player as the winner (noting there are 2 ways to get double numbers)\n", 116 | " for key,value in options.items():\n", 117 | " if value ==1:\n", 118 | " score = 1+np.sum([(x[0]!=x[1]) for x in outcomes if x[2][player] == key])\n", 119 | " outcomes = [x for x in outcomes if x[2][player] != key]\n", 120 | " winner[player] += score \n", 121 | " nochange +=1\n", 122 | " player = (player +1) % 5\n", 123 | " nochange -=1 \n", 124 | " if nochange==0:\n", 125 | " break\n", 126 | "\n", 127 | " if np.sum(winner) == numbers**2:\n", 128 | " print(\"For {} numbers the winner is {}. [{}] Total = {}\".format(numbers,\n", 129 | " players[np.argmax(winner)],\n", 130 | " \" \".join([\"{}:{}/{}\".format(players[i],winner[i],numbers**2) for i in range(len(players))]), \n", 131 | " np.sum(winner) ))\n", 132 | "\n", 133 | " else:\n", 134 | " print(\"===============================\")\n", 135 | " print(\"For {} numbers there is no winner\".format(numbers))\n", 136 | " print(\"===============================\")\n" 137 | ] 138 | }, 139 | { 140 | "cell_type": "code", 141 | "execution_count": 78, 142 | "metadata": {}, 143 | "outputs": [ 144 | { 145 | "name": "stdout", 146 | "output_type": "stream", 147 | "text": [ 148 | "Solution\n", 149 | "~~~~~~~~\n", 150 | "The game does not always end! When N = 15 (or higher), there are numbers Professor Rando could pick such that the game would never end. Congratulations to all of this month’s correct solvers, especially Mdavis, this month’s randomly-selected winner of a Jane Street t-shirt! Correct Submissions: Ted Zong Raymond Lo\n" 151 | ] 152 | } 153 | ], 154 | "source": [ 155 | "url='https://www.janestreet.com/puzzles/solutions/january-2016-solution/'\n", 156 | "res = requests.get(url)\n", 157 | "soup = BeautifulSoup(res.content, 'html.parser')\n", 158 | "x =[text for text in soup.body.stripped_strings]\n", 159 | "\n", 160 | "print(\"Solution\")\n", 161 | "print(\"~~~~~~~~\")\n", 162 | "print(\" \".join(x[7:14]))" 163 | ] 164 | }, 165 | { 166 | "cell_type": "code", 167 | "execution_count": null, 168 | "metadata": {}, 169 | "outputs": [], 170 | "source": [] 171 | } 172 | ], 173 | "metadata": { 174 | "colab": { 175 | "include_colab_link": true, 176 | "name": "JaneSt-Feb18.ipynb", 177 | "provenance": [] 178 | }, 179 | "kernelspec": { 180 | "display_name": "Python 3", 181 | "language": "python", 182 | "name": "python3" 183 | }, 184 | "language_info": { 185 | "codemirror_mode": { 186 | "name": "ipython", 187 | "version": 3 188 | }, 189 | "file_extension": ".py", 190 | "mimetype": "text/x-python", 191 | "name": "python", 192 | "nbconvert_exporter": "python", 193 | "pygments_lexer": "ipython3", 194 | "version": "3.7.7" 195 | } 196 | }, 197 | "nbformat": 4, 198 | "nbformat_minor": 4 199 | } 200 | -------------------------------------------------------------------------------- /2016_06_GetOutTheVote.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "id": "suspected-fusion", 7 | "metadata": {}, 8 | "outputs": [], 9 | "source": [ 10 | "import numpy as np\n", 11 | "import time\n", 12 | "import seaborn as sns\n", 13 | "import matplotlib.pyplot as plt\n", 14 | "import requests\n", 15 | "from bs4 import BeautifulSoup\n", 16 | "import itertools\n", 17 | "from z3 import *\n", 18 | "import math" 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": 2, 24 | "id": "greatest-potter", 25 | "metadata": {}, 26 | "outputs": [ 27 | { 28 | "name": "stdout", 29 | "output_type": "stream", 30 | "text": [ 31 | "Primary elections in the micro-state of Neverland! This state follows the following rules: let D be the number of delegates to be elected, N the total number of votes and Xi the number of ballots cast for each candidate i. Initially, each candidate gets assigned floor(D * Xi/N) delegates where floor() is the standard floor function. Then, the remaining delegates (if any) get assigned in decreasing order of the remainders D*Xi/N – floor(D*Xi/N). That is, if there are K remaining delegates, the K candidates who have the largest remainders D*Xi/N – floor(D*Xi/N) will each receive a delegate. After counting the votes, 102 ballots were found, none of which were invalid or void, and the only three candidates Harry, Larry, and Mary got assigned 2, 2, and 3 delegates respectively. Later on, an audit commission found two mistakes were made: – There were 8 delegates to assign, not 7. – A 103rd ballot had been missed, with an extra vote for Harry. After correcting for those errors, the final results show that Harry lost one delegate. Harry is outraged. How could this happen? How many votes did each candidate receive in this election?\n" 32 | ] 33 | } 34 | ], 35 | "source": [ 36 | "url='https://www.janestreet.com/puzzles/get-out-the-vote/'\n", 37 | "res = requests.get(url)\n", 38 | "soup = BeautifulSoup(res.content, 'html.parser')\n", 39 | "x =[text for text in soup.body.stripped_strings]\n", 40 | "print(\" \".join(x[7:15]))" 41 | ] 42 | }, 43 | { 44 | "cell_type": "code", 45 | "execution_count": 3, 46 | "id": "optimum-begin", 47 | "metadata": {}, 48 | "outputs": [ 49 | { 50 | "name": "stdout", 51 | "output_type": "stream", 52 | "text": [ 53 | "\n", 54 | "The original votes cast are Harry : 20, Larry : 34, Mary :48\n", 55 | "\n", 56 | "Votes/Remainder - Before : ([2, 2, 3], [38, 34, 30]) After : ([1, 3, 4], [65, 66, 75])\n" 57 | ] 58 | } 59 | ], 60 | "source": [ 61 | "def delegates(h,l,m,delegates):\n", 62 | " total = h+l+m\n", 63 | " votes = [h,l,m]\n", 64 | " floor = [x*delegates // total for x in votes]\n", 65 | " remainder = [x*delegates % total for x in votes]\n", 66 | "\n", 67 | " to_allocate = delegates - sum(floor)\n", 68 | " if to_allocate ==1:\n", 69 | " floor[np.argmax(remainder)] +=1\n", 70 | " if to_allocate ==2:\n", 71 | " floor = [x+1 for x in floor]\n", 72 | " floor[np.argmin(remainder)] -=1\n", 73 | " return floor,remainder\n", 74 | "\n", 75 | "for h in range(103):\n", 76 | " for l in range(103-h):\n", 77 | " m= 102-h-l\n", 78 | " if (delegates(h,l,m,7)[0] == [2,2,3]) & (delegates(h+1,l,m,8)[0][0] == 1):\n", 79 | " print(\"\\nThe original votes cast are Harry : {}, Larry : {}, Mary :{}\".format(h,l,m))\n", 80 | " print(\"\\nVotes/Remainder - Before : {} After : {}\".format(delegates(h,l,m,7),delegates(h+1,l,m,8)))" 81 | ] 82 | }, 83 | { 84 | "cell_type": "code", 85 | "execution_count": 4, 86 | "id": "interesting-smile", 87 | "metadata": {}, 88 | "outputs": [ 89 | { 90 | "name": "stdout", 91 | "output_type": "stream", 92 | "text": [ 93 | "The only way in which Harry could have lost a delegate in this fashion would be for the original vote counts to have been (Harry, Larry, Mary) = (20,34,48). Originally Harry’s remainder is the highest of the 3, but after adding the extra delegate and his extra vote, Harry’s becomes the lowest of the 3! Congratulations to all of this month’s correct solvers!\n" 94 | ] 95 | } 96 | ], 97 | "source": [ 98 | "url='https://www.janestreet.com/puzzles/solutions/june-2016-solution/'\n", 99 | "res = requests.get(url)\n", 100 | "soup = BeautifulSoup(res.content, 'html.parser')\n", 101 | "x =[text for text in soup.body.stripped_strings]\n", 102 | "print(\" \".join(x[7:8]))" 103 | ] 104 | }, 105 | { 106 | "cell_type": "code", 107 | "execution_count": null, 108 | "id": "beautiful-maine", 109 | "metadata": {}, 110 | "outputs": [], 111 | "source": [] 112 | } 113 | ], 114 | "metadata": { 115 | "kernelspec": { 116 | "display_name": "Python 3", 117 | "language": "python", 118 | "name": "python3" 119 | }, 120 | "language_info": { 121 | "codemirror_mode": { 122 | "name": "ipython", 123 | "version": 3 124 | }, 125 | "file_extension": ".py", 126 | "mimetype": "text/x-python", 127 | "name": "python", 128 | "nbconvert_exporter": "python", 129 | "pygments_lexer": "ipython3", 130 | "version": "3.7.7" 131 | } 132 | }, 133 | "nbformat": 4, 134 | "nbformat_minor": 5 135 | } 136 | -------------------------------------------------------------------------------- /2017_02_WhatAboutBob.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 7, 6 | "metadata": { 7 | "id": "Oq1xibzMT1Wl" 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "import numpy as np\n", 12 | "import time\n", 13 | "import requests\n", 14 | "from bs4 import BeautifulSoup\n", 15 | "import functools\n", 16 | "from IPython.display import Markdown, display" 17 | ] 18 | }, 19 | { 20 | "cell_type": "code", 21 | "execution_count": 8, 22 | "metadata": { 23 | "id": "ICTfvjZUT1Wr" 24 | }, 25 | "outputs": [ 26 | { 27 | "data": { 28 | "text/markdown": [ 29 | "### What About Bob?\n", 30 | "\n", 31 | "Two players, Alice and Bob, will play a game. Alice chooses any integer from 1 thru 9 (inclusive; all intervals are inclusive). Bob then chooses any integer from 1 thru 9, but can’t pick the number Alice just chose. Then Alice chooses any number from 1 thru 9 but can’t pick the number Bob just chose. They go on in this fashion and keep a running tally of all the chosen numbers so far.\n", 32 | "\n", 33 | "The first player to make this running tally reach exactly N (some positive integer) wins the game. A player can never choose a number that would make the tally be greater than N, and if a player cannot validly choose any numbers under the rules, then he/she loses the game.\n", 34 | "\n", 35 | "To clarify, numbers can potentially be repeated during the game, but just not consecutively. There is no guarantee that Bob will get a turn (for small enough N).\n", 36 | "\n", 37 | "If Alice and Bob each play with perfect strategies, what are the 3 smallest values of N such that Bob wins the game?" 38 | ], 39 | "text/plain": [ 40 | "" 41 | ] 42 | }, 43 | "metadata": {}, 44 | "output_type": "display_data" 45 | } 46 | ], 47 | "source": [ 48 | "url='https://www.janestreet.com/puzzles/what-about-bob/'\n", 49 | "res = requests.get(url)\n", 50 | "soup = BeautifulSoup(res.content, 'html.parser')\n", 51 | "y =[text for text in soup.body.stripped_strings]\n", 52 | "#display([[i,j] for i,j in enumerate(y)])\n", 53 | "display(Markdown(\"### \"+y[6]+\"\\n\\n\"+str(\"\\n\\n\".join(y[7:11]))))" 54 | ] 55 | }, 56 | { 57 | "cell_type": "code", 58 | "execution_count": 3, 59 | "metadata": { 60 | "id": "844RhDFpT1Wu" 61 | }, 62 | "outputs": [ 63 | { 64 | "name": "stdout", 65 | "output_type": "stream", 66 | "text": [ 67 | "For N = 11 Bob wins (11 mod 32)\n", 68 | "For N = 22 Bob wins (22 mod 32)\n", 69 | "For N = 32 Bob wins (0 mod 32)\n", 70 | "For N = 43 Bob wins (11 mod 32)\n", 71 | "For N = 54 Bob wins (22 mod 32)\n", 72 | "For N = 64 Bob wins (0 mod 32)\n", 73 | "For N = 75 Bob wins (11 mod 32)\n", 74 | "For N = 86 Bob wins (22 mod 32)\n", 75 | "For N = 96 Bob wins (0 mod 32)\n", 76 | "For N = 107 Bob wins (11 mod 32)\n", 77 | "Solved in 0.002967 seconds\n" 78 | ] 79 | } 80 | ], 81 | "source": [ 82 | "@functools.lru_cache(maxsize=None)\n", 83 | "def bob_wins(target,last,choices):\n", 84 | " poss = [i for i in choices if i<=target and i != last]\n", 85 | " if target in poss:\n", 86 | " return False\n", 87 | "\n", 88 | " else:\n", 89 | " if all([bob_wins(target-p,p,choices) == False for p in poss]):\n", 90 | " return True\n", 91 | " else:\n", 92 | " return False\n", 93 | " \n", 94 | "start = time.time() \n", 95 | "win_count = 0\n", 96 | "n=0\n", 97 | "values = tuple([i+1 for i in range(9)])\n", 98 | "while win_count < 10:\n", 99 | " n += 1\n", 100 | " if bob_wins(n,0,values):\n", 101 | " win_count += 1\n", 102 | " print(\"For N = {} Bob wins ({} mod 32)\".format(n,n % 32))\n", 103 | "print(\"Solved in {:,.6f} seconds\".format(time.time()-start))" 104 | ] 105 | }, 106 | { 107 | "cell_type": "code", 108 | "execution_count": 19, 109 | "metadata": {}, 110 | "outputs": [ 111 | { 112 | "data": { 113 | "text/markdown": [ 114 | "### February 2017 Solution\n", 115 | "This month’s puzzle proved pretty tricky, and we received many submissions which were very close but not quite right. The smallest 3 numbers for which Bob will win the game are\n", 116 | "\n", 117 | " 11, 22, and 32\n", 118 | "\n", 119 | " . In fact, for larger numbers, Bob will win if N is congruent to 11, 22, or 0 mod 32.\n", 120 | "\n", 121 | " *Alice can obviously win for N=1 up to N=9, and she can also win when N=10 if she says 5 (forcing Bob to pick something other than 5).\n", 122 | "\n", 123 | " *When N=11, Bob wins: If Alice picks some k>1, Bob will be able to pick 11-k. If Alice picks 1, Bob will be able to pick 5.\n", 124 | "\n", 125 | " *For N = 12 up to N = 20, Alice can win by picking the number which gives Bob the tally of N-11. For N = 21, Alice can also win if she picks 5, since Bob must pick something other than 5. If he picks anything other than 8, Alice can win easily. If he picks 8, Alice can counter with 4, Then Bob with 2, then Alice with 1, and thus Alice wins anyway.\n", 126 | "\n", 127 | " *When N = 22, Bob wins, for similar reasons as when N = 11.\n", 128 | "\n", 129 | " *When N = 32, Bob wins as well. If Alice says any k other than 5, Bob can say 10-k. If Alice says 5, Bob can say 8, leaving Alice with a remaining sum of 19 but unable to say 8, which again forces a Bob win." 130 | ], 131 | "text/plain": [ 132 | "" 133 | ] 134 | }, 135 | "metadata": {}, 136 | "output_type": "display_data" 137 | } 138 | ], 139 | "source": [ 140 | "url='https://www.janestreet.com/puzzles/solutions/february-2017-solution/'\n", 141 | "res = requests.get(url)\n", 142 | "soup = BeautifulSoup(res.content, 'html.parser')\n", 143 | "x =[text for text in soup.body.stripped_strings]\n", 144 | "\n", 145 | "\n", 146 | "#display([[i,j] for i,j in enumerate(x)])\n", 147 | "display(Markdown(\"### \"+x[6]+\"\\n\"+str(\"\\n\\n \".join(x[7:15]))))" 148 | ] 149 | }, 150 | { 151 | "cell_type": "code", 152 | "execution_count": null, 153 | "metadata": {}, 154 | "outputs": [], 155 | "source": [] 156 | } 157 | ], 158 | "metadata": { 159 | "colab": { 160 | "include_colab_link": true, 161 | "name": "JaneSt-Feb18.ipynb", 162 | "provenance": [] 163 | }, 164 | "kernelspec": { 165 | "display_name": "Python 3", 166 | "language": "python", 167 | "name": "python3" 168 | }, 169 | "language_info": { 170 | "codemirror_mode": { 171 | "name": "ipython", 172 | "version": 3 173 | }, 174 | "file_extension": ".py", 175 | "mimetype": "text/x-python", 176 | "name": "python", 177 | "nbconvert_exporter": "python", 178 | "pygments_lexer": "ipython3", 179 | "version": "3.7.10" 180 | } 181 | }, 182 | "nbformat": 4, 183 | "nbformat_minor": 4 184 | } 185 | -------------------------------------------------------------------------------- /2017_09_SquareRun.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 3, 6 | "metadata": { 7 | "id": "Oq1xibzMT1Wl" 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "import numpy as np\n", 12 | "import time\n", 13 | "import seaborn as sns\n", 14 | "import matplotlib.pyplot as plt\n", 15 | "import requests\n", 16 | "from bs4 import BeautifulSoup\n", 17 | "import itertools\n", 18 | "from copy import deepcopy as dcopy,copy\n", 19 | "from scipy.ndimage import measurements\n", 20 | "from collections import defaultdict" 21 | ] 22 | }, 23 | { 24 | "cell_type": "code", 25 | "execution_count": 4, 26 | "metadata": { 27 | "id": "ICTfvjZUT1Wr" 28 | }, 29 | "outputs": [ 30 | { 31 | "name": "stdout", 32 | "output_type": "stream", 33 | "text": [ 34 | "A queen is located at a1 and wishes to travel to h8 via a series of one or more moves. (These must be legal queen’s moves.) After each move, the numbers on each of the squares change. If the move is between two spaces which sum to a perfect square, every number on the board decreases by 1 after the move. Otherwise, each number decreases by 5. (The queen may stop on a square more than once.) What is the largest sum you can obtain from the squares you visit over each move in your journey? Please send us your sum and your list of moves. Example: a5 , a3 , b3 , d1 , a1 , d4 , h8 , a1 , h8 would have a sum of 0 + 26 + 29 + 12 – 7 + 17 + 20 – 10 + 18 = 105.\n" 35 | ] 36 | } 37 | ], 38 | "source": [ 39 | "# Another one with no fixed end point.\n", 40 | "\n", 41 | "url='https://www.janestreet.com/puzzles/square-run/'\n", 42 | "res = requests.get(url)\n", 43 | "soup = BeautifulSoup(res.content, 'html.parser')\n", 44 | "x =[text for text in soup.body.stripped_strings]\n", 45 | "\n", 46 | "print(\" \".join(x[7:35]))" 47 | ] 48 | }, 49 | { 50 | "cell_type": "markdown", 51 | "metadata": { 52 | "id": "U9Yh1D3HT1Ws" 53 | }, 54 | "source": [ 55 | "\n", 56 | "" 57 | ] 58 | }, 59 | { 60 | "cell_type": "code", 61 | "execution_count": 5, 62 | "metadata": { 63 | "id": "844RhDFpT1Wu" 64 | }, 65 | "outputs": [ 66 | { 67 | "data": { 68 | "text/plain": [ 69 | "31" 70 | ] 71 | }, 72 | "execution_count": 5, 73 | "metadata": {}, 74 | "output_type": "execute_result" 75 | } 76 | ], 77 | "source": [ 78 | "# Setup the constraints\n", 79 | "fixed = np.rot90([\n", 80 | " [8,5,13,23,29,15,23,30],\n", 81 | " [17,22,30,3,13,25,2,14],\n", 82 | " [10,15,18,28,2,18,27,6],\n", 83 | " [0,31,1,11,22,7,16,20],\n", 84 | " [12,17,24,26,3,24,25,5],\n", 85 | " [27,31,8,11,19,4,12,21],\n", 86 | " [21,20,28,4,9,26,7,14],\n", 87 | " [1,6,9,19,29,10,16,0]\n", 88 | " ],3)\n", 89 | "\n", 90 | "np.max(fixed)" 91 | ] 92 | }, 93 | { 94 | "cell_type": "code", 95 | "execution_count": 6, 96 | "metadata": { 97 | "id": "844RhDFpT1Wu" 98 | }, 99 | "outputs": [], 100 | "source": [ 101 | "class Matrix():\n", 102 | " def __init__(self,fixed):\n", 103 | " self.fixed = fixed\n", 104 | " squares = []\n", 105 | " #for the answer given 0 is a perfect square\n", 106 | " for i in range(0,32):\n", 107 | " squares.append(i**2)\n", 108 | " self.squares = set(squares)\n", 109 | " \n", 110 | " #########################################\n", 111 | " # define possible moves \n", 112 | " def poss_moves(self,x,y): \n", 113 | " temp = []\n", 114 | " for i in range(1,8):\n", 115 | " temp.append([x-i,y+i])\n", 116 | " temp.append([x ,y+i])\n", 117 | " temp.append([x+i,y+i])\n", 118 | " temp.append([x-i,y ])\n", 119 | " temp.append([x+i,y ])\n", 120 | " temp.append([x-i,y-i])\n", 121 | " temp.append([x ,y-i])\n", 122 | " temp.append([x+i,y-i])\n", 123 | " \n", 124 | " ans = []\n", 125 | " for c in temp:\n", 126 | " if np.min(c) < 0 or np.max(c) >7:\n", 127 | " continue\n", 128 | " ans.append(c)\n", 129 | " return ans\n", 130 | "\n", 131 | " \n", 132 | " ##############################################\n", 133 | " # Turn the grid moves into a nice format\n", 134 | " def print_route(self,route):\n", 135 | " letters =[\"a\",\"b\",\"c\",\"d\",\"e\",\"f\",\"g\",\"h\"]\n", 136 | " out = \"\"\n", 137 | " for r,c in route:\n", 138 | " out += letters[r]+str(c+1)+\",\"\n", 139 | " return out\n", 140 | "\n", 141 | " \n", 142 | " ###############################################\n", 143 | " # Main solver. \n", 144 | " def solve(self):\n", 145 | " start = time.perf_counter()\n", 146 | " poss_route = [[[[0,0]], 0, 0 ]] #Route, Score, Points deducted\n", 147 | " max_score = 0\n", 148 | " scores = defaultdict(int) #store the max score for every cell for a given deduction\n", 149 | " \n", 150 | " # loop through the list of possible routes until its empty\n", 151 | " # if the score at given point is better than the previously \n", 152 | " # check if you can jump to the last point. If so check that \n", 153 | " # and if it beats that score then update the best score\n", 154 | " \n", 155 | " while len(poss_route) > 0 :\n", 156 | " route,score,deduct = poss_route.pop()\n", 157 | " g = dcopy(self.fixed) - deduct \n", 158 | " row,col = route[-1]\n", 159 | " \n", 160 | " if scores[((row,col),deduct)] <= score:\n", 161 | " scores[((row,col),deduct)] = score \n", 162 | " start_num = g[row,col]\n", 163 | " end = g[7,7]\n", 164 | " poss_move = self.poss_moves(row,col)\n", 165 | "\n", 166 | " if score + end> max_score:\n", 167 | " if [7,7] in poss_move:\n", 168 | " max_route = route + [[7,7]]\n", 169 | " max_score = score + end\n", 170 | " #print(\"Score = {} Route:{} \".format(max_score,self.print_route(max_route)))\n", 171 | " \n", 172 | " # loop through the possible moves and pull out the\n", 173 | " #value of each cell. Sort by the value of the new \n", 174 | " #cell\n", 175 | " poss_num =[]\n", 176 | " for r,c in poss_move:\n", 177 | " poss_num.append([[r,c],g[r,c]])\n", 178 | " poss_num.sort(key=lambda x:x[1],reverse=True) \n", 179 | " \n", 180 | " \n", 181 | " # loop through the paths while there are positive numbers in \n", 182 | " # the grid\n", 183 | " for x,new_num in poss_num:\n", 184 | " r,c = x\n", 185 | " total = new_num + start_num\n", 186 | " if total <0:\n", 187 | " continue \n", 188 | " #if np.sum(g >0) ==0:\n", 189 | " # continue\n", 190 | " if total in self.squares:\n", 191 | " new_deduct = deduct + 1\n", 192 | " else:\n", 193 | " new_deduct = deduct + 5\n", 194 | "\n", 195 | " x = [route + [[r,c]], score+new_num, new_deduct]\n", 196 | " poss_route.insert(0,x)\n", 197 | " \n", 198 | " print(\"***Solved in {:.4f} seconds***\\n\".format(time.perf_counter() - start)) \n", 199 | " print(\"Final Score = {} Route: {}\".format(max_score,self.print_route(max_route)))\n", 200 | " " 201 | ] 202 | }, 203 | { 204 | "cell_type": "code", 205 | "execution_count": 7, 206 | "metadata": { 207 | "colab": { 208 | "base_uri": "https://localhost:8080/", 209 | "height": 374 210 | }, 211 | "id": "zexQ8t1kT1W3", 212 | "outputId": "7c2aec4e-d2fb-45d8-fc7e-679051faafed" 213 | }, 214 | "outputs": [ 215 | { 216 | "name": "stdout", 217 | "output_type": "stream", 218 | "text": [ 219 | "***Solved in 44.8287 seconds***\n", 220 | "\n", 221 | "Final Score = 305 Route: a1,c3,c7,d8,d1,g4,h3,a3,a4,e8,f7,b3,a3,e7,e5,b5,c4,f4,d4,f2,a2,c2,c7,d8,g8,e8,h8,d4,h8,\n" 222 | ] 223 | } 224 | ], 225 | "source": [ 226 | "test = Matrix(fixed)\n", 227 | "test.solve()" 228 | ] 229 | }, 230 | { 231 | "cell_type": "code", 232 | "execution_count": 58, 233 | "metadata": {}, 234 | "outputs": [ 235 | { 236 | "name": "stdout", 237 | "output_type": "stream", 238 | "text": [ 239 | "The largest sum possible for the queen’s journey is 305 . It turns out that there are 36 paths that achieve such a score, one of which is: c3, c7, d8, d1, g4, h3, a3, a4, e8, f7, b3, a3, e7, e5, b5, c4, f4, d4, f2, c2, c8, e8, b5, c4, c2, b3, f7, e8, h8 , which scores as 8 + 29 + 21 + 16 + 21 + 16 + 21 + 5 + 21 + 16 + 21 + 16 + 1 + 9 + 17 + 9 + 8 + 9 + 8 + 9 – 7 + 8 + 9 + 1 + 4 + 6 – 1 + 2 + 2 = 305 . Maximum scores from:\n" 240 | ] 241 | } 242 | ], 243 | "source": [ 244 | "url='https://www.janestreet.com/puzzles/solutions/september-2017-solution/'\n", 245 | "res = requests.get(url)\n", 246 | "soup = BeautifulSoup(res.content, 'html.parser')\n", 247 | "x =[text for text in soup.body.stripped_strings]\n", 248 | "\n", 249 | "print(\" \".join(x[7:18]))" 250 | ] 251 | }, 252 | { 253 | "cell_type": "code", 254 | "execution_count": null, 255 | "metadata": {}, 256 | "outputs": [], 257 | "source": [] 258 | } 259 | ], 260 | "metadata": { 261 | "colab": { 262 | "include_colab_link": true, 263 | "name": "JaneSt-Feb18.ipynb", 264 | "provenance": [] 265 | }, 266 | "kernelspec": { 267 | "display_name": "Python 3", 268 | "language": "python", 269 | "name": "python3" 270 | }, 271 | "language_info": { 272 | "codemirror_mode": { 273 | "name": "ipython", 274 | "version": 3 275 | }, 276 | "file_extension": ".py", 277 | "mimetype": "text/x-python", 278 | "name": "python", 279 | "nbconvert_exporter": "python", 280 | "pygments_lexer": "ipython3", 281 | "version": "3.7.10" 282 | } 283 | }, 284 | "nbformat": 4, 285 | "nbformat_minor": 4 286 | } 287 | -------------------------------------------------------------------------------- /2018_06_TwentyFourSeven.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import numpy as np\n", 10 | "import time\n", 11 | "from skimage.morphology import label\n", 12 | "import requests\n", 13 | "from bs4 import BeautifulSoup" 14 | ] 15 | }, 16 | { 17 | "cell_type": "code", 18 | "execution_count": 2, 19 | "metadata": {}, 20 | "outputs": [ 21 | { 22 | "name": "stdout", 23 | "output_type": "stream", 24 | "text": [ 25 | "The grid is incomplete. Place numbers in some of the empty cells below so that in total the grid contains one 1, two 2’s, etc., up to seven 7’s. Furthermore, each row and column must contain exactly 4 numbers which sum to 20. Finally, the numbered cells must form a connected region*, but every 2-by-2 subsquare in the completed grid must contain at least one empty cell.The answer to this puzzle is the product of the areas of the connected groups of empty squares in the completed grid.(*Updated 2018-06-14 to clarify that cells connect alongedges only.)\n" 26 | ] 27 | } 28 | ], 29 | "source": [ 30 | "# Going back to look at an old puzzle to try to get started on the Dec 2020\n", 31 | "# Use backtracking to solve\n", 32 | "\n", 33 | "\n", 34 | "url='https://www.janestreet.com/puzzles/twenty-four-seven/'\n", 35 | "res = requests.get(url)\n", 36 | "soup = BeautifulSoup(res.content, 'html.parser')\n", 37 | "x =[text for text in soup.body.stripped_strings]\n", 38 | "\n", 39 | "print(\"\".join(x[7:]))" 40 | ] 41 | }, 42 | { 43 | "cell_type": "markdown", 44 | "metadata": {}, 45 | "source": [ 46 | "" 47 | ] 48 | }, 49 | { 50 | "cell_type": "code", 51 | "execution_count": 3, 52 | "metadata": {}, 53 | "outputs": [], 54 | "source": [ 55 | "grid =np.array([[0,4,0,0,0,0,0],\n", 56 | " [0,0,6,3,0,0,6],\n", 57 | " [0,0,0,0,0,5,5],\n", 58 | " [0,0,0,4,0,0,0],\n", 59 | " [4,7,0,0,0,0,0],\n", 60 | " [2,0,0,7,4,0,0],\n", 61 | " [0,0,0,0,0,1,0]])\n", 62 | "\n", 63 | "\n", 64 | "grid[grid==0]=-1" 65 | ] 66 | }, 67 | { 68 | "cell_type": "code", 69 | "execution_count": 4, 70 | "metadata": {}, 71 | "outputs": [], 72 | "source": [ 73 | "#Forming the Puzzle Grid\n", 74 | "class Matrix():\n", 75 | " def __init__(self,puzzle_string):\n", 76 | " self.grid = puzzle_string\n", 77 | " self.counts = {i:i-np.sum(self.grid==i) for i in range(8)}\n", 78 | " print('The problem - Start {}'.format(time.strftime('%X')))\n", 79 | " self.printGrid()\n", 80 | " \n", 81 | "#Function to print the grid\n", 82 | " def printGrid(self):\n", 83 | " print(self.grid)\n", 84 | " \n", 85 | "#Function to check if a digit can be placed in the given block\n", 86 | "#Various checks\n", 87 | " def possible(self,row,col,digit):\n", 88 | "\n", 89 | " # Check row and columns count\n", 90 | " if digit ==0:\n", 91 | " if np.sum(self.grid[:,col]==0) > 2:\n", 92 | " return False\n", 93 | " if np.sum(self.grid[row,:]==0) > 2:\n", 94 | " return False\n", 95 | " \n", 96 | " # Check that final number completes\n", 97 | " if np.sum(self.grid[row,:]==-1) ==1:\n", 98 | " if np.sum(self.grid[row,:])+digit+1 != 20:\n", 99 | " return False\n", 100 | " \n", 101 | " if np.sum(self.grid[:,col]==-1) ==1:\n", 102 | " if np.sum(self.grid[:,col])+digit+1 != 20:\n", 103 | " return False\n", 104 | " \n", 105 | " # Check row and columns sum\n", 106 | " if np.sum(self.grid[:,col])+digit+np.sum(self.grid[:,col]==-1) > 20:\n", 107 | " return False\n", 108 | " \n", 109 | " if np.sum(self.grid[row,:])+digit+np.sum(self.grid[row,:]==-1) > 20:\n", 110 | " return False\n", 111 | " \n", 112 | " #check 2x2\n", 113 | " if digit !=0:\n", 114 | " if self.twobytwo(row,col):\n", 115 | " return False\n", 116 | " \n", 117 | " # Check Fobidden nums\n", 118 | " if self.more_constraints(self.grid[row,:],digit):\n", 119 | " return False\n", 120 | " \n", 121 | " if self.more_constraints(self.grid[:,col],digit):\n", 122 | " return False\n", 123 | " \n", 124 | " #checkconnected\n", 125 | " if np.max(label(self.grid!=0,connectivity=1)) >1 :\n", 126 | " return False\n", 127 | " \n", 128 | " # All tests pass return True\n", 129 | " return True\n", 130 | " \n", 131 | " def twobytwo(self,i,j):\n", 132 | " if (i > 0 and j > 0 and \n", 133 | " self.grid[i-1,j-1] > 0 and \n", 134 | " self.grid[i-1,j] > 0 and \n", 135 | " self.grid[i,j-1] > 0):\n", 136 | " return True\n", 137 | " \n", 138 | " if (i > 0 and j < 6 and\n", 139 | " self.grid[i-1,j+1] > 0 and \n", 140 | " self.grid[i-1,j] > 0 and \n", 141 | " self.grid[i,j+1] > 0):\n", 142 | " return True\n", 143 | " \n", 144 | " if (i < 6 and j > 0 and\n", 145 | " self.grid[i+1,j-1] > 0 and \n", 146 | " self.grid[i+1,j] > 0 and \n", 147 | " self.grid[i,j-1] > 0):\n", 148 | " return True\n", 149 | "\n", 150 | " \n", 151 | " if (i < 6 and j < 6 and\n", 152 | " self.grid[i+1,j+1] > 0 and \n", 153 | " self.grid[i+1,j] > 0 and \n", 154 | " self.grid[i,j+1] > 0):\n", 155 | " return True\n", 156 | " \n", 157 | " return False\n", 158 | " \n", 159 | " def more_constraints(self,array,digit):\n", 160 | " \n", 161 | " if np.sum(array==1) > 0:\n", 162 | " if digit < 5:\n", 163 | " return True\n", 164 | " return False\n", 165 | " \n", 166 | " if np.sum(array==2) > 0:\n", 167 | " if digit < 4 :\n", 168 | " return True\n", 169 | " return False\n", 170 | " \n", 171 | " if np.sum(array==3) > 0:\n", 172 | " if digit < 3 :\n", 173 | " return True\n", 174 | " return False\n", 175 | " \n", 176 | " if np.sum(array==4) > 0:\n", 177 | " if digit < 2 :\n", 178 | " return True\n", 179 | " return False\n", 180 | " \n", 181 | " return False\n", 182 | " \n", 183 | "\n", 184 | " def solve(self):\n", 185 | " \n", 186 | " for row in range(7):\n", 187 | " for col in range(7):\n", 188 | " if self.grid[row][col] == -1:\n", 189 | " for digit in range(7,-1,-1):\n", 190 | " if (digit ==0 or self.counts[digit]>0):\n", 191 | " if self.possible(row,col,digit):\n", 192 | " self.grid[row,col] = digit\n", 193 | " self.counts[digit] -= 1\n", 194 | " self.solve()\n", 195 | " self.grid[row,col] = -1 #Backtrack step\n", 196 | " self.counts[digit] += 1\n", 197 | " return False\n", 198 | " \n", 199 | " print('\\nThe solution - End {}'.format(time.strftime('%X')))\n", 200 | " self.printGrid()" 201 | ] 202 | }, 203 | { 204 | "cell_type": "code", 205 | "execution_count": 5, 206 | "metadata": {}, 207 | "outputs": [ 208 | { 209 | "name": "stdout", 210 | "output_type": "stream", 211 | "text": [ 212 | "The problem - Start 09:31:55\n", 213 | "[[-1 4 -1 -1 -1 -1 -1]\n", 214 | " [-1 -1 6 3 -1 -1 6]\n", 215 | " [-1 -1 -1 -1 -1 5 5]\n", 216 | " [-1 -1 -1 4 -1 -1 -1]\n", 217 | " [ 4 7 -1 -1 -1 -1 -1]\n", 218 | " [ 2 -1 -1 7 4 -1 -1]\n", 219 | " [-1 -1 -1 -1 -1 1 -1]]\n", 220 | "\n", 221 | "The solution - End 09:32:24\n", 222 | "[[7 4 3 0 6 0 0]\n", 223 | " [0 0 6 3 5 0 6]\n", 224 | " [0 0 5 0 5 5 5]\n", 225 | " [0 3 6 4 0 0 7]\n", 226 | " [4 7 0 0 0 7 2]\n", 227 | " [2 0 0 7 4 7 0]\n", 228 | " [7 6 0 6 0 1 0]]\n", 229 | "\n", 230 | " Finish 09:39:25\n" 231 | ] 232 | } 233 | ], 234 | "source": [ 235 | "x = Matrix(grid)\n", 236 | "x.solve()\n", 237 | "print('\\n Finish {}'.format(time.strftime('%X')))" 238 | ] 239 | }, 240 | { 241 | "cell_type": "markdown", 242 | "metadata": {}, 243 | "source": [ 244 | "### Solution is :\n", 245 | "\n", 246 | "" 247 | ] 248 | }, 249 | { 250 | "cell_type": "code", 251 | "execution_count": null, 252 | "metadata": {}, 253 | "outputs": [], 254 | "source": [] 255 | }, 256 | { 257 | "cell_type": "code", 258 | "execution_count": null, 259 | "metadata": {}, 260 | "outputs": [], 261 | "source": [] 262 | } 263 | ], 264 | "metadata": { 265 | "kernelspec": { 266 | "display_name": "Python 3", 267 | "language": "python", 268 | "name": "python3" 269 | }, 270 | "language_info": { 271 | "codemirror_mode": { 272 | "name": "ipython", 273 | "version": 3 274 | }, 275 | "file_extension": ".py", 276 | "mimetype": "text/x-python", 277 | "name": "python", 278 | "nbconvert_exporter": "python", 279 | "pygments_lexer": "ipython3", 280 | "version": "3.7.10" 281 | } 282 | }, 283 | "nbformat": 4, 284 | "nbformat_minor": 4 285 | } 286 | -------------------------------------------------------------------------------- /2018_06_TwentyFourSeven_Z3.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import pandas as pd\n", 10 | "import numpy as np\n", 11 | "import time\n", 12 | "import seaborn as sns\n", 13 | "from matplotlib.colors import ListedColormap\n", 14 | "import matplotlib.pyplot as plt\n", 15 | "import requests\n", 16 | "from bs4 import BeautifulSoup\n", 17 | "import itertools\n", 18 | "from copy import deepcopy as dcopy,copy\n", 19 | "from scipy.ndimage import measurements\n", 20 | "from skimage.morphology import label\n", 21 | "\n", 22 | "from z3 import *\n", 23 | "from IPython.display import Markdown, display" 24 | ] 25 | }, 26 | { 27 | "cell_type": "code", 28 | "execution_count": 2, 29 | "metadata": {}, 30 | "outputs": [ 31 | { 32 | "data": { 33 | "text/markdown": [ 34 | "**Twenty Four Seven**\n", 35 | "\n", 36 | "The grid is incomplete. Place numbers in some of the empty cells below so that in total the grid contains one 1, two 2’s, etc., up to seven 7’s. Furthermore, each row and column must contain exactly 4 numbers which sum to 20. Finally, the numbered cells must form a connected region*, but every 2-by-2 subsquare in the completed grid must contain at least one empty cell. The answer to this puzzle is the product of the areas of the connected groups of empty squares in the completed grid. (*Updated 2018-06-14 to clarify that cells connect along edges only .)" 37 | ], 38 | "text/plain": [ 39 | "" 40 | ] 41 | }, 42 | "metadata": {}, 43 | "output_type": "display_data" 44 | } 45 | ], 46 | "source": [ 47 | "url='https://www.janestreet.com/puzzles/twenty-four-seven/'\n", 48 | "res = requests.get(url)\n", 49 | "soup = BeautifulSoup(res.content, 'html.parser')\n", 50 | "\n", 51 | "y =[text for text in soup.body.stripped_strings]\n", 52 | "#display([[i,j] for i,j in enumerate(y)])\n", 53 | "display(Markdown(\"**\"+y[6]+\"**\\n\\n\"+str(\" \".join(y[7:12]))))" 54 | ] 55 | }, 56 | { 57 | "cell_type": "markdown", 58 | "metadata": {}, 59 | "source": [ 60 | "" 61 | ] 62 | }, 63 | { 64 | "cell_type": "code", 65 | "execution_count": 3, 66 | "metadata": {}, 67 | "outputs": [], 68 | "source": [ 69 | "grid =np.array([[0,4,0,0,0,0,0],\n", 70 | " [0,0,6,3,0,0,6],\n", 71 | " [0,0,0,0,0,5,5],\n", 72 | " [0,0,0,4,0,0,0],\n", 73 | " [4,7,0,0,0,0,0],\n", 74 | " [2,0,0,7,4,0,0],\n", 75 | " [0,0,0,0,0,1,0]])\n", 76 | "\n", 77 | "\n", 78 | "grid[grid==0] =-1" 79 | ] 80 | }, 81 | { 82 | "cell_type": "code", 83 | "execution_count": 4, 84 | "metadata": {}, 85 | "outputs": [], 86 | "source": [ 87 | "def neighbours( i, j):\n", 88 | " l=[]\n", 89 | " if i-1 >= 0:\n", 90 | " l.append((i-1,j))\n", 91 | " if i+1 < 7:\n", 92 | " l.append((i+1,j))\n", 93 | " if j-1 >= 0:\n", 94 | " l.append((i,j-1))\n", 95 | " if j+1 < 7:\n", 96 | " l.append((i,j+1))\n", 97 | " return l\n", 98 | "\n", 99 | "def areas(grid):\n", 100 | " labels, num = measurements.label(np.logical_not(grid!=0))\n", 101 | " areas = measurements.sum(np.logical_not(grid!=0), labels, index=range(1, num+1))\n", 102 | " return np.prod(areas)" 103 | ] 104 | }, 105 | { 106 | "cell_type": "code", 107 | "execution_count": 16, 108 | "metadata": {}, 109 | "outputs": [ 110 | { 111 | "name": "stdout", 112 | "output_type": "stream", 113 | "text": [ 114 | "8 solutions took 0.2144 seconds\n", 115 | "\n" 116 | ] 117 | }, 118 | { 119 | "data": { 120 | "image/png": "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\n", 121 | "text/plain": [ 122 | "
" 123 | ] 124 | }, 125 | "metadata": { 126 | "needs_background": "light" 127 | }, 128 | "output_type": "display_data" 129 | }, 130 | { 131 | "data": { 132 | "text/markdown": [ 133 | "### Score is 240" 134 | ], 135 | "text/plain": [ 136 | "" 137 | ] 138 | }, 139 | "metadata": {}, 140 | "output_type": "display_data" 141 | } 142 | ], 143 | "source": [ 144 | "\n", 145 | "grid[grid==0] =-1\n", 146 | " \n", 147 | "start = time.time()\n", 148 | "s = Solver()\n", 149 | "\n", 150 | "X = [[Int(\"X_%s%s\" % (i+1,j+1)) for j in range(7) ] for i in range(7) ]\n", 151 | "\n", 152 | "# limit the values and place the fixed values\n", 153 | "s += [And(X[i][j]>=0,X[i][j] <=7) for j in range(7) for i in range(7) ]\n", 154 | "s += [X[i][j] == int(grid[i,j]) for j in range(7) for i in range(7) if grid[i,j] > 0]\n", 155 | "\n", 156 | "# Row/Col Sums\n", 157 | "s += [Sum([X[i][j] for i in range(7)]) == 20 for j in range(7)]\n", 158 | "s += [Sum([X[i][j] for j in range(7)]) == 20 for i in range(7)]\n", 159 | "\n", 160 | "s += [PbEq([(X[i][j] ==0,1) for i in range(7)],3) for j in range(7)]\n", 161 | "s += [PbEq([(X[i][j] ==0,1) for j in range(7)],3) for i in range(7)]\n", 162 | " \n", 163 | "# only n instances of n \n", 164 | "for n in range(1,8):\n", 165 | " s += PbEq([(X[i][j] == n,1) for j in range(7) for i in range(7)],n)\n", 166 | "\n", 167 | "# no 2x2\n", 168 | "s += [Or(X[i][j] ==0,X[i+1][j] ==0,X[i][j+1] ==0,X[i+1][j+1] ==0) for j in range(6) for i in range(6)]\n", 169 | "\n", 170 | "# at least one neighbour. Cut down the connectivity a bit.\n", 171 | "s += [Implies(X[i][j] !=0,Or([X[k][l] !=0 for (k,l) in neighbours(i,j)])) for j in range(7) for i in range(7) ] \n", 172 | " \n", 173 | "\n", 174 | "# coding up the connectivity is hard so just solve and test then exclude solutions that are not connected\n", 175 | "count = 0\n", 176 | "while True: \n", 177 | " if s.check() == sat:\n", 178 | " count += 1\n", 179 | " m = s.model()\n", 180 | " x = np.array([[m.evaluate(X[i][j]).as_long() for j in range(7)] for i in range(7)])\n", 181 | " if np.max(label(x != 0,connectivity=1)) == 1 :\n", 182 | " break\n", 183 | " s += Or([X[i][j]!=int(x[i,j]) for j in range(7) for i in range(7)])\n", 184 | " else:\n", 185 | " print(\"failed to solve\") \n", 186 | " break\n", 187 | " \n", 188 | "print('{} solutions took {:0.4f} seconds\\n'.format(count,time.time()-start))\n", 189 | " \n", 190 | "fig,ax = plt.subplots(1,1,figsize=(3,3)) \n", 191 | "y = np.array(x).astype('int').astype('str')\n", 192 | "y[y==\"0\"] =\"\"\n", 193 | "ax =sns.heatmap(x==grid,annot=y,cbar=False,cmap=\"Greys\",fmt=\"\",linewidths=2,center= 1,linecolor=\"grey\",annot_kws={\"size\":14})\n", 194 | "ax.axis(\"off\")\n", 195 | "plt.tight_layout()\n", 196 | "plt.show()\n", 197 | "display(Markdown('### Score is {:,.0f}'.format((areas(x)))))" 198 | ] 199 | }, 200 | { 201 | "cell_type": "code", 202 | "execution_count": 6, 203 | "metadata": {}, 204 | "outputs": [ 205 | { 206 | "data": { 207 | "text/markdown": [ 208 | "### June 2018 solution\n", 209 | "\n", 210 | "The completed grid is shown to the left. The product of the areas of the regions of blank squares is\n", 211 | "240\n", 212 | ".\n", 213 | "Congrats to everyone who solved this month’s puzzle!" 214 | ], 215 | "text/plain": [ 216 | "" 217 | ] 218 | }, 219 | "metadata": {}, 220 | "output_type": "display_data" 221 | } 222 | ], 223 | "source": [ 224 | "url='https://www.janestreet.com/puzzles/solutions/june-2018-solution/'\n", 225 | "res = requests.get(url)\n", 226 | "soup = BeautifulSoup(res.content, 'html.parser')\n", 227 | "y =[text for text in soup.body.stripped_strings]\n", 228 | "\n", 229 | "display(Markdown(\"### \"+y[6]+\"\\n\\n\"+str(\"\\n\".join(y[7:11]))))" 230 | ] 231 | }, 232 | { 233 | "cell_type": "markdown", 234 | "metadata": {}, 235 | "source": [ 236 | "" 237 | ] 238 | }, 239 | { 240 | "cell_type": "code", 241 | "execution_count": null, 242 | "metadata": {}, 243 | "outputs": [], 244 | "source": [] 245 | }, 246 | { 247 | "cell_type": "code", 248 | "execution_count": null, 249 | "metadata": {}, 250 | "outputs": [], 251 | "source": [] 252 | } 253 | ], 254 | "metadata": { 255 | "kernelspec": { 256 | "display_name": "Python 3", 257 | "language": "python", 258 | "name": "python3" 259 | }, 260 | "language_info": { 261 | "codemirror_mode": { 262 | "name": "ipython", 263 | "version": 3 264 | }, 265 | "file_extension": ".py", 266 | "mimetype": "text/x-python", 267 | "name": "python", 268 | "nbconvert_exporter": "python", 269 | "pygments_lexer": "ipython3", 270 | "version": "3.7.10" 271 | } 272 | }, 273 | "nbformat": 4, 274 | "nbformat_minor": 4 275 | } 276 | -------------------------------------------------------------------------------- /2020_01_AlterNate.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": { 7 | "id": "Oq1xibzMT1Wl" 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "import numpy as np\n", 12 | "import time\n", 13 | "import requests\n", 14 | "from bs4 import BeautifulSoup\n", 15 | "import functools\n", 16 | "from IPython.display import Markdown, display" 17 | ] 18 | }, 19 | { 20 | "cell_type": "code", 21 | "execution_count": 21, 22 | "metadata": { 23 | "id": "ICTfvjZUT1Wr" 24 | }, 25 | "outputs": [ 26 | { 27 | "data": { 28 | "text/markdown": [ 29 | "### Alter/Nate\n", 30 | "\n", 31 | "Two friends, Alter and Nate, have a conversation:\n", 32 | "\n", 33 | "Alter: Nate, let’s play a game. I’ll pick an integer between 1 and 10 (inclusive), then you’ll pick an integer between 1 and 10 (inclusive), and then I’ll go again, then you’ll go again, and so on and so forth. We’ll keep adding our numbers together to make a running total. And whoever makes the running total be greater than or equal to 100 loses. You go first.\n", 34 | "\n", 35 | "Nate: That’s not fair! Whenever I pick a number X, you’ll just pick 11-X, and then I’ll always get stuck with 99 and I’ll make the total go greater than 100.\n", 36 | "\n", 37 | "Alter: Ok fine. New rule then, no one can pick a number that would make the sum of that number and the previous number equal to 11. You still go first. Now can we play?\n", 38 | "\n", 39 | "Nate: Um… sure.\n", 40 | "\n", 41 | "Who wins, and what is their strategy?" 42 | ], 43 | "text/plain": [ 44 | "" 45 | ] 46 | }, 47 | "metadata": {}, 48 | "output_type": "display_data" 49 | } 50 | ], 51 | "source": [ 52 | "url='https://www.janestreet.com/puzzles/alter-nate/'\n", 53 | "res = requests.get(url)\n", 54 | "soup = BeautifulSoup(res.content, 'html.parser')\n", 55 | "y =[text for text in soup.body.stripped_strings]\n", 56 | "#display([[i,j] for i,j in enumerate(y)])\n", 57 | "display(Markdown(\"### \"+y[6]+\"\\n\\n\"+str(\"\\n\\n\".join(y[7:13]))))" 58 | ] 59 | }, 60 | { 61 | "cell_type": "code", 62 | "execution_count": 23, 63 | "metadata": { 64 | "id": "844RhDFpT1Wu" 65 | }, 66 | "outputs": [ 67 | { 68 | "name": "stdout", 69 | "output_type": "stream", 70 | "text": [ 71 | "For N = 3 Nate wins\n", 72 | "Solved in 0.001996 seconds\n" 73 | ] 74 | } 75 | ], 76 | "source": [ 77 | "# copying code from \"what about bob\" ... alter to \"first player to hit 99 wins\"\n", 78 | "# start with the first move having been made\n", 79 | "\n", 80 | "@functools.lru_cache(maxsize=None)\n", 81 | "def nate_wins(target,last,choices):\n", 82 | " poss = [i for i in choices if i<=target and i != 11-last]\n", 83 | " # if the next move can win then the next player wins(Alter in the first move)\n", 84 | " if target in poss:\n", 85 | " return False\n", 86 | " else:\n", 87 | " # if all of the next +1 moves win (return= false) then the player next +1 wins (Nate on the first loop)\n", 88 | " if all([nate_wins(target-p,p,choices) == False for p in poss]):\n", 89 | " return True\n", 90 | " else:\n", 91 | " return False\n", 92 | "\n", 93 | "start = time.time() \n", 94 | "values = tuple([i+1 for i in range(10)])\n", 95 | "\n", 96 | "for n in range(1,11):\n", 97 | " if nate_wins(99-n,n,values):\n", 98 | " print(\"For N = {} Nate wins\".format(n))\n", 99 | "\n", 100 | "print(\"Solved in {:,.6f} seconds\".format(time.time()-start))" 101 | ] 102 | }, 103 | { 104 | "cell_type": "code", 105 | "execution_count": 22, 106 | "metadata": {}, 107 | "outputs": [ 108 | { 109 | "name": "stdout", 110 | "output_type": "stream", 111 | "text": [ 112 | "Solution\n", 113 | "~~~~~~~~\n", 114 | "Nate (the first player) can always win in this game, by starting with the number 3.    After this first turn, Nate can force the running total to increment by units of 12.  This could happen 2 different ways: If Alter picks some number X between 2 and 10, Nate chooses 12-X If Alter picks 1, Nate responds by picking 1 as well.  Now Alter cannot pick 10 (since this would force the sum of the previous two numbers to be 11), and must pick some other number Y.  Nate then picks 10-Y. In this way, Nate can force Alter to choose numbers when the running total is equal to 3, 15, 27, 39, 51, 63, 75, 87, and 99.  At this point, Alter is forced to take the total to 100 or greater. Congratulations to everyone who solved this month’s puzzle!\n" 115 | ] 116 | } 117 | ], 118 | "source": [ 119 | "url='https://www.janestreet.com/puzzles/solutions/january-2020-solution/'\n", 120 | "res = requests.get(url)\n", 121 | "soup = BeautifulSoup(res.content, 'html.parser')\n", 122 | "x =[text for text in soup.body.stripped_strings]\n", 123 | "\n", 124 | "print(\"Solution\")\n", 125 | "print(\"~~~~~~~~\")\n", 126 | "print(\" \".join(x[7:13]))" 127 | ] 128 | }, 129 | { 130 | "cell_type": "code", 131 | "execution_count": null, 132 | "metadata": {}, 133 | "outputs": [], 134 | "source": [] 135 | }, 136 | { 137 | "cell_type": "code", 138 | "execution_count": null, 139 | "metadata": {}, 140 | "outputs": [], 141 | "source": [] 142 | } 143 | ], 144 | "metadata": { 145 | "colab": { 146 | "include_colab_link": true, 147 | "name": "JaneSt-Feb18.ipynb", 148 | "provenance": [] 149 | }, 150 | "kernelspec": { 151 | "display_name": "Python 3", 152 | "language": "python", 153 | "name": "python3" 154 | }, 155 | "language_info": { 156 | "codemirror_mode": { 157 | "name": "ipython", 158 | "version": 3 159 | }, 160 | "file_extension": ".py", 161 | "mimetype": "text/x-python", 162 | "name": "python", 163 | "nbconvert_exporter": "python", 164 | "pygments_lexer": "ipython3", 165 | "version": "3.7.10" 166 | } 167 | }, 168 | "nbformat": 4, 169 | "nbformat_minor": 4 170 | } 171 | -------------------------------------------------------------------------------- /2020_07_WhatATrit.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 9, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "# Do imports\n", 10 | "import numpy as np\n", 11 | "import time\n", 12 | "import requests\n", 13 | "from bs4 import BeautifulSoup" 14 | ] 15 | }, 16 | { 17 | "cell_type": "code", 18 | "execution_count": 12, 19 | "metadata": {}, 20 | "outputs": [ 21 | { 22 | "name": "stdout", 23 | "output_type": "stream", 24 | "text": [ 25 | "Puzzle\n", 26 | "~~~~~~\n", 27 | "The country of Terniquaternaria follows an unusual numeral system. Its numbers are expressed as in base 4, but there are only three “digits”: 0, 1 and T, which has value (-1). For instance, the number “1T01” corresponds to the number 49 in the usual decimal system (i.e. +1*4^3-1*4^2+0*4^1+1*4^0). Due to the lack of a 4th digit, many numbers, such as 29, have no direct representation. Instead, the inhabitants of Terniquaternaria (called “Trits”) express them as the quotient of two representable numbers (when possible). For instance, 29 could be written as (1100T/1TT) or (11111T/1T0T), among other variants. Decimus just landed in Terniquaternaria and needs to write down his passport number (524293) on the admission form. Is it possible? If so, how can it be written? If not, can you prove impossibility?\n" 28 | ] 29 | } 30 | ], 31 | "source": [ 32 | "url='https://www.janestreet.com/puzzles/what-a-trit/'\n", 33 | "res = requests.get(url)\n", 34 | "soup = BeautifulSoup(res.content, 'html.parser')\n", 35 | "x =[text for text in soup.body.stripped_strings]\n", 36 | "print(\"Puzzle\")\n", 37 | "print(\"~~~~~~\")\n", 38 | "print(\" \".join(x[7:9]))" 39 | ] 40 | }, 41 | { 42 | "cell_type": "code", 43 | "execution_count": 45, 44 | "metadata": {}, 45 | "outputs": [], 46 | "source": [ 47 | "#Define conversion functions - Decimal to Trit and Trit to decimal .. approximate where the digit should be 2.\n", 48 | "def trit2dec(string):\n", 49 | " x=0\n", 50 | " number = 0\n", 51 | " for i in reversed(string):\n", 52 | " if i =='T':\n", 53 | " i=-1\n", 54 | " number += int(i)*4**x\n", 55 | " x +=1\n", 56 | " return number\n", 57 | "\n", 58 | "dig2str = {1: '1', -1: 'T', 0: '0'} \n", 59 | "\n", 60 | "def dec2trit(n):\n", 61 | " def totrit(n):\n", 62 | " if n == 0: return []\n", 63 | " if (n % 4) == 0: \n", 64 | " return [0] + totrit(n // 4)\n", 65 | " if (n % 4) == 1: \n", 66 | " return [1] + totrit(n // 4)\n", 67 | " if (n % 4) == 2: \n", 68 | " return [1] + totrit(n // 4)\n", 69 | " if (n % 4) == 3: \n", 70 | " return [-1] + totrit((n + 1) // 4)\n", 71 | " return \"\".join(dig2str[d] for d in reversed(totrit(n)) ) \n", 72 | "\n", 73 | "#speed up the search by only using valid trits! Convert to balanced ternary and use that as the divisor.\n", 74 | "def dec2baltern(n):\n", 75 | " def tobt(n):\n", 76 | " if n == 0: return []\n", 77 | " if (n % 3) == 0: \n", 78 | " return [0] + tobt(n // 3)\n", 79 | " if (n % 3) == 1: \n", 80 | " return [1] + tobt(n // 3)\n", 81 | " if (n % 3) == 2: \n", 82 | " return [-1] + tobt((n + 1) // 3)\n", 83 | " return \"\".join(dig2str[d] for d in reversed(tobt(n)) ) \n", 84 | " " 85 | ] 86 | }, 87 | { 88 | "cell_type": "code", 89 | "execution_count": null, 90 | "metadata": {}, 91 | "outputs": [ 92 | { 93 | "name": "stdout", 94 | "output_type": "stream", 95 | "text": [ 96 | "7 - The solution is 111/1T or 21/3= 7 in decimal\n", 97 | "7 - Done at 09:50:39\n", 98 | "13 - The solution is 1T1/1 or 13/1= 13 in decimal\n", 99 | "13 - Done at 09:50:39\n", 100 | "37 - The solution is 1TT00T/11T or 703/19= 37 in decimal\n", 101 | "37 - Done at 09:50:39\n", 102 | "133 - The solution is 1TTT10001/111T1 or 44,289/333= 133 in decimal\n", 103 | "133 - Done at 09:50:39\n", 104 | "517 - The solution is 1TTTT1T00T11/1111T01 or 2,804,725/5,425= 517 in decimal\n", 105 | "517 - Done at 09:50:39\n", 106 | "2053 - The solution is 1TTTTT1TT00T011/11111T001 or 179,089,349/87,233= 2,053 in decimal\n", 107 | "2053 - Done at 09:50:40\n", 108 | "8197 - The solution is 1TTTTTT1TTT00T0011/111111T0001 or 11,455,348,485/1,397,505= 8,197 in decimal\n", 109 | "8197 - Done at 09:50:46\n", 110 | "32773 - The solution is 1TTTTTTT1TTTT00T00011/1111111T00001 or 733,041,327,109/22,367,233= 32,773 in decimal\n", 111 | "32773 - Done at 09:51:58\n", 112 | "131077 - The solution is 1TTTTTTTT1TTTTT00T000011/11111111T000001 or 46,913,033,072,645/357,904,385= 131,077 in decimal\n", 113 | "131077 - Done at 10:07:40\n" 114 | ] 115 | } 116 | ], 117 | "source": [ 118 | "# Brute force calculate the first few solutions for numbers of the form (4^i)*2 + 5\n", 119 | "\n", 120 | "for i in range(10):\n", 121 | " test = (4**i)*2 +5\n", 122 | " #print(test) \n", 123 | " for a in range(1,70000000):\n", 124 | " balternary = dec2baltern(a)\n", 125 | " y = trit2dec(balternary)\n", 126 | " x = dec2trit(test*y)\n", 127 | " y = dec2trit(y)\n", 128 | " x_dec = trit2dec(x)\n", 129 | " y_dec = trit2dec(y)\n", 130 | "\n", 131 | " #print(x,x_dec)\n", 132 | " #print(y,y_dec)\n", 133 | " if x_dec/y_dec == test:\n", 134 | " print('{} - The solution is {}/{} or {:,}/{:,} = {:,.0f} in decimal'.format(test,x,y,x_dec,y_dec,x_dec/y_dec))\n", 135 | " break\n", 136 | " print('{} - Done at {}'.format(test,time.strftime('%X')))" 137 | ] 138 | }, 139 | { 140 | "cell_type": "code", 141 | "execution_count": 46, 142 | "metadata": {}, 143 | "outputs": [ 144 | { 145 | "name": "stdout", 146 | "output_type": "stream", 147 | "text": [ 148 | "The solution is 3,002,408,341,848,069/5,726,584,833 = 524,293 in decimal\n" 149 | ] 150 | } 151 | ], 152 | "source": [ 153 | "#Follow the same pattern\n", 154 | "x_dec = trit2dec('1TTTTTTTTT1TTTTTT00T0000011')\n", 155 | "y_dec = trit2dec('111111111T0000001')\n", 156 | "print('The solution is {:,}/{:,} = {:,.0f} in decimal'.format(x_dec,y_dec,x_dec/y_dec))" 157 | ] 158 | }, 159 | { 160 | "cell_type": "code", 161 | "execution_count": 8, 162 | "metadata": {}, 163 | "outputs": [ 164 | { 165 | "name": "stdout", 166 | "output_type": "stream", 167 | "text": [ 168 | "Solution\n", 169 | "~~~~~~~~\n", 170 | "This month’s puzzle was in fact solvable, and had multiple correct solutions.  One example is 1TTTTTTTTT1TTTTTT00T0000011/111111111T0000001 .  These trits, when converted to base 10, represent the ratio 3002408341848069/5726584833 = 524293.  It turns out every odd integer can be written as a ratio of representable numbers, although the proof is far from trivial, here is a paper about it.\n" 171 | ] 172 | } 173 | ], 174 | "source": [ 175 | "\n", 176 | "url='https://www.janestreet.com/puzzles/solutions/july-2020-solution/'\n", 177 | "res = requests.get(url)\n", 178 | "soup = BeautifulSoup(res.content, 'html.parser')\n", 179 | "x =[text for text in soup.body.stripped_strings]\n", 180 | "print(\"Solution\")\n", 181 | "print(\"~~~~~~~~\")\n", 182 | "print(\" \".join(x[7:12]))" 183 | ] 184 | }, 185 | { 186 | "cell_type": "code", 187 | "execution_count": null, 188 | "metadata": {}, 189 | "outputs": [], 190 | "source": [] 191 | } 192 | ], 193 | "metadata": { 194 | "kernelspec": { 195 | "display_name": "Python 3", 196 | "language": "python", 197 | "name": "python3" 198 | }, 199 | "language_info": { 200 | "codemirror_mode": { 201 | "name": "ipython", 202 | "version": 3 203 | }, 204 | "file_extension": ".py", 205 | "mimetype": "text/x-python", 206 | "name": "python", 207 | "nbconvert_exporter": "python", 208 | "pygments_lexer": "ipython3", 209 | "version": "3.7.7" 210 | } 211 | }, 212 | "nbformat": 4, 213 | "nbformat_minor": 4 214 | } 215 | -------------------------------------------------------------------------------- /2020_08_StudyAndPonder.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import numpy as np\n", 10 | "import pandas as pd\n", 11 | "import requests\n", 12 | "from bs4 import BeautifulSoup" 13 | ] 14 | }, 15 | { 16 | "cell_type": "code", 17 | "execution_count": 5, 18 | "metadata": {}, 19 | "outputs": [ 20 | { 21 | "name": "stdout", 22 | "output_type": "stream", 23 | "text": [ 24 | "Problem\n", 25 | "~~~~~~~\n", 26 | "Study & Ponder Oh no, which one is missing?\n", 27 | "\n", 28 | "AMDISHBANTMUSBACMAAPLDOSPGRMNVDALKQCOMCDWBAXPNRGPNCMSIVBWABCDNSCHWECLXLNXRXELINCYUMSFTNTAPDVNOVMCOFBHSICERNVRSNPSWKSUDREXRAYAEEOGCHTROPXDDRILMNOWFCCITWDCNPVHALLEGISRGPSADBENEMNSTSCOSTTWODFLTMOSLGWWHRLNTRSGEXPEGDLRCXOMLMTPRGOOGLWMTDGXCAHONLSNKETNFITBRK.BIOCBREQIXUNHFCCLCOPEPNWLTWTRVLOWSTZTSNADISCAGILDVAREGNBLLYBDXCMGMMMCKHCATOXYLNCLHXCMIN\n" 29 | ] 30 | } 31 | ], 32 | "source": [ 33 | "#get the puzzle text (scraping isn't optimal, probably easier to copy paste !) \n", 34 | "url='https://www.janestreet.com/puzzles/study-ponder/'\n", 35 | "res = requests.get(url)\n", 36 | "soup = BeautifulSoup(res.content, 'html.parser')\n", 37 | "soup_p = soup.body\n", 38 | "x =[text for text in soup_p.stripped_strings]\n", 39 | "puzzle = \"\".join(x[8:])\n", 40 | "\n", 41 | "#get the list of S&P500 tickers from wikipedia. \n", 42 | "ticker_url='https://en.wikipedia.org/wiki/List_of_S%26P_500_companies#S&P_500_component_stocks'\n", 43 | "df=pd.read_html(ticker_url, header=0)[0]\n", 44 | "tickers = df['Symbol']\n", 45 | "\n", 46 | "print(\"Problem\")\n", 47 | "print(\"~~~~~~~\")\n", 48 | "print(\" \".join(x[6:8]))\n", 49 | "print(\"\")\n", 50 | "print(\"\".join(x[8:16]))" 51 | ] 52 | }, 53 | { 54 | "cell_type": "code", 55 | "execution_count": 3, 56 | "metadata": {}, 57 | "outputs": [ 58 | { 59 | "name": "stdout", 60 | "output_type": "stream", 61 | "text": [ 62 | "Ticker SPGI is missing\n" 63 | ] 64 | } 65 | ], 66 | "source": [ 67 | "# Work out which one is missing by looping through the tickers\n", 68 | "for ticker in tickers:\n", 69 | " #print('Found {} at {}'.format(ticker,puzzle.find(ticker)) )\n", 70 | " if puzzle.find(ticker) == -1:\n", 71 | " print('Ticker {} is missing'.format(ticker))" 72 | ] 73 | }, 74 | { 75 | "cell_type": "code", 76 | "execution_count": 28, 77 | "metadata": {}, 78 | "outputs": [ 79 | { 80 | "name": "stdout", 81 | "output_type": "stream", 82 | "text": [ 83 | "Solution\n", 84 | "~~~~~~~~\n", 85 | "S tudying & P ondering, the solver noticed that there were a few stock tickers among the jumble of letters.   After that the “Aha!” of the letters being a string containing all but one of the S&P 500 index members would lead to a search ending with SPGI , the ticker of the corporation responsible for the index, a satisfyingly “meta” solution. The following solvers made it to the end of this puzzle correctly, nice work!\n" 86 | ] 87 | } 88 | ], 89 | "source": [ 90 | "url='https://www.janestreet.com/puzzles/solutions/august-2020-solution/'\n", 91 | "res = requests.get(url)\n", 92 | "soup = BeautifulSoup(res.content, 'html.parser')\n", 93 | "soup_p = soup.body\n", 94 | "x =[text for text in soup_p.stripped_strings]\n", 95 | "print(\"Solution\")\n", 96 | "print(\"~~~~~~~~\")\n", 97 | "print(\" \".join(x[8:16]))\n", 98 | "\n" 99 | ] 100 | }, 101 | { 102 | "cell_type": "code", 103 | "execution_count": 4, 104 | "metadata": {}, 105 | "outputs": [ 106 | { 107 | "name": "stdout", 108 | "output_type": "stream", 109 | "text": [ 110 | "0 Jane Street\n", 111 | "1 Puzzles\n", 112 | "2 Intro\n", 113 | "3 Current Puzzle\n", 114 | "4 Archive\n", 115 | "5 Puzzle Archive\n", 116 | "6 Study & Ponder\n", 117 | "7 Oh no, which one is missing?\n", 118 | "8 AMDISHBANTMUSBACMAAPLDOSPGRMNVDALKQCOMCDW\n", 119 | "9 BAXPNRGPNCMSIVBWABCDNSCHWECLXLNXRXELINCYUMS\n", 120 | "10 FTNTAPDVNOVMCOFBHSICERNVRSNPSWKSUDREXRAYAEEO\n", 121 | "11 GCHTROPXDDRILMNOWFCCITWDCNPVHALLEGISRGPSADBE\n", 122 | "12 NEMNSTSCOSTTWODFLTMOSLGWWHRLNTRSGEXPEGDLRC\n", 123 | "13 XOMLMTPRGOOGLWMTDGXCAHONLSNKETNFITBRK.\n", 124 | "14 BIOCBREQIXUNHFCCLCOPEPNWLTWTRVLOWSTZTSNADISC\n", 125 | "15 AGILDVAREGNBLLYBDXCMGMMMCKHCATOXYLNCLHXCMIN\n", 126 | "16 TCBOEWELLCMCSALGNLOKCMEXPDOWUNPFEBAYCHRWAT\n", 127 | "17 URINTUNMHKLACPRTGTJXJPMRKFRCTVAONUEVRGDEDLTR\n", 128 | "18 OLFRTDYAMZNEEFXAEPWRKMXIMSCIEXCPBCTSHWMBKN\n", 129 | "19 GJNJCINFOXAIVZIONDAQDUKSSULTAESSLBF.\n", 130 | "20 BBYDHRBLKOTISYYAMCRMDLZBRAMETROWYNNIDXXUHSY\n", 131 | "21 FANGUALXNOCFGJNPRUAALBXPKIMASBUXAFLIRMKCTASY\n", 132 | "22 KMIQVRTXAMTBKRJFTIFFIVRSKMBSXAPAYCTLABMDTEIXA\n", 133 | "23 MATVIACNCTXSEEQRVORCLVSJMARFMCHPEAKAMPCHDHII\n", 134 | "24 PGPCARROSTXNFLXADPZBHBIIBMYLUVTRHIGSREMROKEY\n", 135 | "25 SORLYVFCSCOTYLENWSAPHMKTXTFXHSTELANETFCXUPSX\n", 136 | "26 HPQHOLXAOSHESPYPLDISCKAPTVJKHYAVYAJGPPLABTADS\n", 137 | "27 KAIGJBHTHASHLTCOGPKGAVGOPPGAIZADMABBVANSSFAS\n", 138 | "28 TFTVAVBPAYXAWKCVSFDXDOVFLSFISVAZOHUMAMGNWRB\n", 139 | "29 COOPFGCVXCSXDFS\n" 140 | ] 141 | } 142 | ], 143 | "source": [ 144 | "for i,x in enumerate(soup.body.stripped_strings):\n", 145 | " print(i,x)" 146 | ] 147 | }, 148 | { 149 | "cell_type": "code", 150 | "execution_count": null, 151 | "metadata": {}, 152 | "outputs": [], 153 | "source": [] 154 | } 155 | ], 156 | "metadata": { 157 | "kernelspec": { 158 | "display_name": "Python 3", 159 | "language": "python", 160 | "name": "python3" 161 | }, 162 | "language_info": { 163 | "codemirror_mode": { 164 | "name": "ipython", 165 | "version": 3 166 | }, 167 | "file_extension": ".py", 168 | "mimetype": "text/x-python", 169 | "name": "python", 170 | "nbconvert_exporter": "python", 171 | "pygments_lexer": "ipython3", 172 | "version": "3.7.7" 173 | } 174 | }, 175 | "nbformat": 4, 176 | "nbformat_minor": 4 177 | } 178 | -------------------------------------------------------------------------------- /2020_10&11_CandyCollectors.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 2, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import numpy as np\n", 10 | "import math\n", 11 | "import pandas as pd\n", 12 | "import itertools\n", 13 | "import requests\n", 14 | "from bs4 import BeautifulSoup\n", 15 | "import sympy" 16 | ] 17 | }, 18 | { 19 | "cell_type": "code", 20 | "execution_count": 10, 21 | "metadata": {}, 22 | "outputs": [ 23 | { 24 | "name": "stdout", 25 | "output_type": "stream", 26 | "text": [ 27 | "Puzzle\n", 28 | "~~~~~~\n", 29 | "Five children went trick-or-treating together and decided to randomly split their candy haul at the end of the night.  As it turned out, they got a total of 25 pieces of candy, 5 copies each of 5 different types (they live in a small town).  They distribute the candies by choosing an ordering of the 25 uniformly at random from all shufflings, and then giving the first 5 to the first child, the second 5 to the second, and so on.What is the probability that each child has one type of candy that they have strictly more of than every other trick-or-treater? Give your (exact!) answer in a lowest terms fraction.November update:correct solutions to this puzzle have come in more slowly than others, so we are going to keep it up for an extra month and will have a new puzzle on the site in early December.\n" 30 | ] 31 | } 32 | ], 33 | "source": [ 34 | "print(\"Puzzle\")\n", 35 | "print(\"~~~~~~\")\n", 36 | "url='https://www.janestreet.com/puzzles/candy-collectors/'\n", 37 | "res = requests.get(url)\n", 38 | "soup = BeautifulSoup(res.content, 'html.parser')\n", 39 | "x =[text for text in soup.body.stripped_strings]\n", 40 | "print(\"\".join(x[7:11]))" 41 | ] 42 | }, 43 | { 44 | "cell_type": "markdown", 45 | "metadata": {}, 46 | "source": [ 47 | "
\n", 48 | "Generating Function :\n", 49 | "
\n", 50 | "
\n", 51 | "$\\begin{align}\n", 52 | "f_1(a,b,c,d,e) &= \\frac{a^5}{5!}+\\frac{a^4}{4!}(b+c+d+e)+\\\\\n", 53 | "&\\quad\\frac{a^3}{3!}\\left(\\frac{b^2}{2!}+\\frac{c^2}{2!}+\\frac{d^2}{2!}+\\frac{e^2}{2!}+bc+bd+be+cd+ce+de\\right)\n", 54 | "\\\\&\\quad\\frac{a^2}{2!}(bcd+bce+bde+cde)\n", 55 | "\\end{align}$" 56 | ] 57 | }, 58 | { 59 | "cell_type": "code", 60 | "execution_count": 4, 61 | "metadata": {}, 62 | "outputs": [ 63 | { 64 | "name": "stdout", 65 | "output_type": "stream", 66 | "text": [ 67 | "Answer is\n", 68 | "~~~~~~~~~\n", 69 | "318,281,087 / 8,016,470,462\n" 70 | ] 71 | } 72 | ], 73 | "source": [ 74 | "# Here's the solution using generating functions as per stackexchange\n", 75 | "# Don't think this is the easiest way or the way that is described above\n", 76 | "# https://math.stackexchange.com/questions/3934750/how-to-solve-this-candy-collectors-puzzle\n", 77 | "# and the book referred to https://www2.math.upenn.edu/~wilf/DownldGF.html\n", 78 | "\n", 79 | "def f(a,b,c,d,e) :\n", 80 | " return (a**5/math.factorial(5) \n", 81 | " + a**4/math.factorial(4)*(b + c + d + e) \n", 82 | " + a**3/math.factorial(3)*((b**2+c**2+d**2+e**2)/math.factorial(2)+ b* c + b *d + b* e + c* d + c* e + d *e)\n", 83 | " + a**2/math.factorial(2)*(b* c* d + b* c* e + b* d* e + c* d *e))\n", 84 | " \n", 85 | "\n", 86 | "from sympy.abc import a,b,c,d,e\n", 87 | "EGF = sympy.Poly(f(a,b,c,d,e)*f(b,c,d,e,a)*f(c,d,e,a,b)*f(d,e,a,b,c)*f(e,a,b,c,d))\n", 88 | "coeff = EGF.coeff_monomial((a*b*c*d*e)**5) \n", 89 | "\n", 90 | "num = int(coeff * math.factorial(5)**6)\n", 91 | "denom = int(math.factorial(25)/(math.factorial(5)**5))\n", 92 | "gcd= math.gcd(num,denom)\n", 93 | "\n", 94 | "print(\"Answer is\")\n", 95 | "print(\"~~~~~~~~~\")\n", 96 | "print(\"{:,.0f} / {:,.0f}\".format(num/gcd,denom/gcd))" 97 | ] 98 | }, 99 | { 100 | "cell_type": "code", 101 | "execution_count": 12, 102 | "metadata": {}, 103 | "outputs": [ 104 | { 105 | "name": "stdout", 106 | "output_type": "stream", 107 | "text": [ 108 | "\n", 109 | "Solution\n", 110 | "~~~~~~\n", 111 | "The probability that every child would have strictly more of a candy than each other trick-or-treater was precisely 318281087/8016470462 , which comes to just under 4%.  The most straightforward way to compute this was to assign a particular candy to each child and compute the probability of that (writing some code certainly helped here) and then multiplying the result by 120 to account for the disjoint other assignments of candy to trick-or-treater.\n" 112 | ] 113 | } 114 | ], 115 | "source": [ 116 | "print(\"\")\n", 117 | "print(\"Solution\")\n", 118 | "print(\"~~~~~~\")\n", 119 | "url='https://www.janestreet.com/puzzles/solutions/october-2020-solution/'\n", 120 | "res = requests.get(url)\n", 121 | "soup = BeautifulSoup(res.content, 'html.parser')\n", 122 | "x =[text for text in soup.body.stripped_strings]\n", 123 | "print(\" \".join(x[7:10]))" 124 | ] 125 | }, 126 | { 127 | "cell_type": "code", 128 | "execution_count": null, 129 | "metadata": {}, 130 | "outputs": [], 131 | "source": [] 132 | } 133 | ], 134 | "metadata": { 135 | "kernelspec": { 136 | "display_name": "Python 3", 137 | "language": "python", 138 | "name": "python3" 139 | }, 140 | "language_info": { 141 | "codemirror_mode": { 142 | "name": "ipython", 143 | "version": 3 144 | }, 145 | "file_extension": ".py", 146 | "mimetype": "text/x-python", 147 | "name": "python", 148 | "nbconvert_exporter": "python", 149 | "pygments_lexer": "ipython3", 150 | "version": "3.7.7" 151 | } 152 | }, 153 | "nbformat": 4, 154 | "nbformat_minor": 4 155 | } 156 | -------------------------------------------------------------------------------- /2021_01_FigurineFiguring.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "id": "burning-shift", 7 | "metadata": {}, 8 | "outputs": [], 9 | "source": [ 10 | "import numpy as np\n", 11 | "import time\n", 12 | "import math\n", 13 | "import itertools\n", 14 | "import requests\n", 15 | "from bs4 import BeautifulSoup\n", 16 | "import sympy\n", 17 | "import copy\n", 18 | "import xlwings as xw\n", 19 | "from sympy.abc import a" 20 | ] 21 | }, 22 | { 23 | "cell_type": "code", 24 | "execution_count": 2, 25 | "id": "living-dragon", 26 | "metadata": {}, 27 | "outputs": [ 28 | { 29 | "name": "stdout", 30 | "output_type": "stream", 31 | "text": [ 32 | "Puzzle\n", 33 | "~~~~~~\n", 34 | "Jane received 78 figurines as gifts this holiday season:  12 drummers drumming, 11 pipers piping, 10 lords a-leaping, etc., down to 1 partridge in a pear tree.   They are all mixed together in a big bag.  She agrees with her friend Alex that this seems like too many figurines for one person to have, so she decides to give some of her figurines to Alex.   Jane will uniformly randomly pull figurines out of the bag one at a time until she pulls out the partridge in a pear tree, and will give Alex all of the figurines she pulled out of the bag (except the partridge, that’s Jane’s favorite).Ifnis the maximum number of any one type of ornament that Alex gets, what is the expected value ofn, to seven significant figures?\n" 35 | ] 36 | } 37 | ], 38 | "source": [ 39 | "# I found this stackexchange post very informative\n", 40 | "# https://math.stackexchange.com/questions/3934750/how-to-solve-this-candy-collectors-puzzle\n", 41 | "# and the book referred to https://www2.math.upenn.edu/~wilf/DownldGF.html is vey good.\n", 42 | "\n", 43 | "print(\"Puzzle\")\n", 44 | "print(\"~~~~~~\")\n", 45 | "url='https://www.janestreet.com/puzzles/figurine-figuring/'\n", 46 | "res = requests.get(url)\n", 47 | "soup = BeautifulSoup(res.content, 'html.parser')\n", 48 | "x =[text for text in soup.body.stripped_strings]\n", 49 | "print(\"\".join(x[7:13]))" 50 | ] 51 | }, 52 | { 53 | "cell_type": "markdown", 54 | "id": "widespread-specification", 55 | "metadata": {}, 56 | "source": [ 57 | "
\n", 58 | "Generating function is :\n", 59 | "
\n", 60 | "
\n", 61 | " $\\Large\\prod_{j=2}^i\\left(\\sum_{k=0}^j \\frac{j!}{(j-k)!} \\frac{a^k}{k!}\\right)$" 62 | ] 63 | }, 64 | { 65 | "cell_type": "code", 66 | "execution_count": 3, 67 | "id": "adjacent-mainstream", 68 | "metadata": {}, 69 | "outputs": [ 70 | { 71 | "name": "stdout", 72 | "output_type": "stream", 73 | "text": [ 74 | "With a max of 1 of any ball you can draw 11\n", 75 | "With a max of 2 of any ball you can draw 22\n", 76 | "With a max of 3 of any ball you can draw 32\n", 77 | "With a max of 4 of any ball you can draw 41\n", 78 | "With a max of 5 of any ball you can draw 49\n", 79 | "With a max of 6 of any ball you can draw 56\n", 80 | "With a max of 7 of any ball you can draw 62\n", 81 | "With a max of 8 of any ball you can draw 67\n", 82 | "With a max of 9 of any ball you can draw 71\n", 83 | "With a max of 10 of any ball you can draw 74\n", 84 | "With a max of 11 of any ball you can draw 76\n", 85 | "With a max of 12 of any ball you can draw 77\n", 86 | "(12, 78)\n" 87 | ] 88 | } 89 | ], 90 | "source": [ 91 | "# Generating function using sympy and put the coeffs into an array\n", 92 | "# remove higher order terms to find probabilites with constraints on \n", 93 | "# max of any one figure\n", 94 | "\n", 95 | "perms=np.zeros((12,78))\n", 96 | "\n", 97 | "for cap in range(1,13):\n", 98 | " gfa = sympy.poly(1,a)\n", 99 | " for j in range(2,13):\n", 100 | " gfb = sympy.poly(0,a)\n", 101 | " for k in range(0,min(j+1,cap+1)):\n", 102 | " gfb += sympy.poly((math.factorial(j)/math.factorial(j-k))*(a**k)/math.factorial(k),a)\n", 103 | " gfa *=gfb\n", 104 | " print(\"With a max of {} of any ball you can draw {}\".format(cap,len(gfa.monoms())-1)) \n", 105 | " perms[cap-1,78-len(gfa.coeffs()):]=gfa.coeffs()\n", 106 | "\n", 107 | "print(np.array(perms).shape)" 108 | ] 109 | }, 110 | { 111 | "cell_type": "code", 112 | "execution_count": 4, 113 | "id": "mineral-swedish", 114 | "metadata": {}, 115 | "outputs": [ 116 | { 117 | "name": "stdout", 118 | "output_type": "stream", 119 | "text": [ 120 | "expectation for 2 pulls = 1.0977443609022557\n", 121 | "From the generating function = 1.0977443609022557\n" 122 | ] 123 | } 124 | ], 125 | "source": [ 126 | "# Checking a simple case\n", 127 | "# - expectation for 2 pulls longhand\n", 128 | "# - then extract from the matrix of coeffs to check\n", 129 | "\n", 130 | "grid = np.zeros((11,11))\n", 131 | "\n", 132 | "for i,j in itertools.product(range(11),range(11)):\n", 133 | " grid[i,j] = (i+2)/77*(j+2-(i==j))/76\n", 134 | " \n", 135 | "print(\"expectation for 2 pulls =\",1+np.sum(grid * np.eye(11)))\n", 136 | "\n", 137 | "x = perms[0,-3] # ways of pulling 2 with a cap of 1 => x/y is expectation of 1\n", 138 | "y = perms[1,-3] # ways of pulling 2 with a cap of 2 => (y-x)/y is expectation of 2\n", 139 | "\n", 140 | "print(\"From the generating function =\",((2*(y-x)+x)/y))" 141 | ] 142 | }, 143 | { 144 | "cell_type": "code", 145 | "execution_count": 5, 146 | "id": "signal-fault", 147 | "metadata": {}, 148 | "outputs": [ 149 | { 150 | "name": "stdout", 151 | "output_type": "stream", 152 | "text": [ 153 | "Expectation is : 6.859787\n" 154 | ] 155 | } 156 | ], 157 | "source": [ 158 | "# Generalizing the above for the whole matrix\n", 159 | "# lopping off the last column as it represents 0 draws\n", 160 | "# 1 is equally likely to be drawn at every point so divide by 78\n", 161 | "\n", 162 | "processed = np.zeros((12,78))\n", 163 | "processed[0,:]=perms[0,:]\n", 164 | "for i in range(11,0,-1):\n", 165 | " processed[i,:] = perms[i,:] - perms [i-1,:]\n", 166 | "processed = (processed/np.sum(processed,axis=0))[:,:77]*np.array([np.arange(1,13)]).T\n", 167 | "\n", 168 | "print(\"Expectation is : {:.6f}\".format(np.sum(processed)/78))" 169 | ] 170 | }, 171 | { 172 | "cell_type": "code", 173 | "execution_count": 6, 174 | "id": "guilty-survey", 175 | "metadata": {}, 176 | "outputs": [ 177 | { 178 | "name": "stdout", 179 | "output_type": "stream", 180 | "text": [ 181 | "This month’s puzzle required a careful computation to remain tractable.  One approach that would work: by keeping track of the position of the partridge figurine and the maximum count of identical figurines drawn before it as the twelve different sets of figurines were inserted into the permutation reduced the space into a matrix of size 78 x 12.  The final expected value was approximately 6.859787\n" 182 | ] 183 | } 184 | ], 185 | "source": [ 186 | "url='https://www.janestreet.com/puzzles/solutions/january-2021-solution/'\n", 187 | "res = requests.get(url)\n", 188 | "soup = BeautifulSoup(res.content, 'html.parser')\n", 189 | "x =[text for text in soup.body.stripped_strings]\n", 190 | "\n", 191 | "print(\" \".join(x[7:9]))" 192 | ] 193 | }, 194 | { 195 | "cell_type": "code", 196 | "execution_count": 8, 197 | "id": "facial-airfare", 198 | "metadata": {}, 199 | "outputs": [ 200 | { 201 | "name": "stdout", 202 | "output_type": "stream", 203 | "text": [ 204 | "9.0\n", 205 | "6.85959514040486\n", 206 | "6.859817070091465\n", 207 | "6.858856713714429\n", 208 | "6.859196785200804\n", 209 | "6.858591228281754\n", 210 | "6.858106190315635\n", 211 | "6.859182305831099\n", 212 | "6.859125392609326\n", 213 | "6.859076237880418\n", 214 | "\n", 215 | " Solution took 433.4376 seconds\n", 216 | "\n" 217 | ] 218 | } 219 | ], 220 | "source": [ 221 | "# Check with a simulation to see if I'm close\n", 222 | "\n", 223 | "urn = np.array(\n", 224 | " [1,\n", 225 | " 2,2,\n", 226 | " 3,3,3,\n", 227 | " 4,4,4,4,\n", 228 | " 5,5,5,5,5,\n", 229 | " 6,6,6,6,6,6,\n", 230 | " 7,7,7,7,7,7,7,\n", 231 | " 8,8,8,8,8,8,8,8,\n", 232 | " 9,9,9,9,9,9,9,9,9,\n", 233 | " 10,10,10,10,10,10,10,10,10,10,\n", 234 | " 11,11,11,11,11,11,11,11,11,11,11,\n", 235 | " 12,12,12,12,12,12,12,12,12,12,12,12],dtype=int)\n", 236 | "\n", 237 | "def loop():\n", 238 | " temp = copy.copy(urn)\n", 239 | " np.random.shuffle(temp)\n", 240 | " picks = np.zeros(11)\n", 241 | " for draw in temp:\n", 242 | " if draw == 1:\n", 243 | " return np.max(picks)\n", 244 | " else:\n", 245 | " picks[draw-2] +=1\n", 246 | " \n", 247 | "start = time.perf_counter()\n", 248 | "sum = 0\n", 249 | "for i in range(10000000):\n", 250 | " sum += loop()\n", 251 | " if i % 1000000 ==0:\n", 252 | " print(sum/(i+1))\n", 253 | " \n", 254 | "stop = time.perf_counter()\n", 255 | "print('\\n Solution took {:0.4f} seconds\\n'.format((stop-start)))" 256 | ] 257 | }, 258 | { 259 | "cell_type": "code", 260 | "execution_count": null, 261 | "id": "greek-cologne", 262 | "metadata": {}, 263 | "outputs": [], 264 | "source": [] 265 | } 266 | ], 267 | "metadata": { 268 | "kernelspec": { 269 | "display_name": "Python 3", 270 | "language": "python", 271 | "name": "python3" 272 | }, 273 | "language_info": { 274 | "codemirror_mode": { 275 | "name": "ipython", 276 | "version": 3 277 | }, 278 | "file_extension": ".py", 279 | "mimetype": "text/x-python", 280 | "name": "python", 281 | "nbconvert_exporter": "python", 282 | "pygments_lexer": "ipython3", 283 | "version": "3.7.7" 284 | } 285 | }, 286 | "nbformat": 4, 287 | "nbformat_minor": 5 288 | } 289 | -------------------------------------------------------------------------------- /2021_05_PastTens.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": { 7 | "id": "Oq1xibzMT1Wl" 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "import numpy as np\n", 12 | "import time\n", 13 | "import requests\n", 14 | "import math\n", 15 | "import itertools\n", 16 | "from bs4 import BeautifulSoup\n", 17 | "from fractions import Fraction\n", 18 | "from IPython.display import Markdown, display" 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": 2, 24 | "metadata": { 25 | "id": "ICTfvjZUT1Wr" 26 | }, 27 | "outputs": [ 28 | { 29 | "data": { 30 | "text/markdown": [ 31 | "### Past Tens\n", 32 | "\n", 33 | "Show Solution\n", 34 | "\n", 35 | "Find the answer to the hidden question in this picture.\n", 36 | "\n", 37 | "(Download or open in a new tab to see it in full resolution.)\n", 38 | "\n", 39 | "Addendum:\n", 40 | "\n", 41 | "The V-shaped wedge near the righthand side is not a hint, it’s just how the records settled." 42 | ], 43 | "text/plain": [ 44 | "" 45 | ] 46 | }, 47 | "metadata": {}, 48 | "output_type": "display_data" 49 | } 50 | ], 51 | "source": [ 52 | "url='https://www.janestreet.com/puzzles/past-tens-index/'\n", 53 | "res = requests.get(url)\n", 54 | "soup = BeautifulSoup(res.content, 'html.parser')\n", 55 | "y =[text for text in soup.body.stripped_strings]\n", 56 | "#display([(i,j) for i,j in enumerate(y)])\n", 57 | "display(Markdown(\"### \"+y[7]+\"\\n\\n\"+str(\"\\n\\n\".join(y[9:14]))))\n", 58 | "\n", 59 | "# Didn't get this one. Spotted the out of order records and almost worked it out but, missed the record between Cassius and Chance and didn't put the \"The\" on \n", 60 | "# Dark side of the moon and slider when I wrote them down so it didn't leap out\n", 61 | "# Both corrected below" 62 | ] 63 | }, 64 | { 65 | "cell_type": "markdown", 66 | "metadata": {}, 67 | "source": [ 68 | "\n", 69 | "\n" 70 | ] 71 | }, 72 | { 73 | "cell_type": "code", 74 | "execution_count": 3, 75 | "metadata": {}, 76 | "outputs": [ 77 | { 78 | "name": "stdout", 79 | "output_type": "stream", 80 | "text": [ 81 | "whats The band for the #1 1973 Song My Love\n" 82 | ] 83 | } 84 | ], 85 | "source": [ 86 | "vinyl =[['ABC','The lexicon of love'],['Air ','Music For Museum'],['apollo 100','apollo 100'],['the avalanches','since I left you'],['baccara','baccara'],\n", 87 | " ['william basinski','92982'],['chris bell','I am the cosmos'],['blackalicous','blazing arrow'],['david bowie ','low'],['marvin gaye','whats going on']\n", 88 | " ,['the blue nile','hats'],['luiz bonfa','luiz bonfa'],['the books','the way out'],['cassius','au reve'],['?','?'],['Chance ','in search'],['chromatics','night drive']\n", 89 | " ,['the clientele','suburban light'],['galaxie 500','on fire'],['pink floyd','The dark side of the moon'],['colored music','colored music'],['daft punk','discovery'],\n", 90 | " ['dip in the pool ','On retinae'],['echo and the bunnymen','ocean rain'],['brian eno','apollo atmosphears and sound tracks'],['the exploding hearts','guitar romantic'],\n", 91 | " ['FAD','FAD'],['faust','faust iv'],['joy division','unknown pleasures'],['paul mcartney ','band on the run'],['The Firey Furnaces','EP'],\n", 92 | " [' Klaus Schulze & Cosmic Jokers','galactic supermarket'],['manuel gittsching','e2-e4'],['ragnar grippe','sand'],['bobby humphrey','fancy dancer'],\n", 93 | " ['Inoyama Land','Danzindan​-​Pojidon'],['jj','no2'],['grace jones','nightclubbing'],['my bloody valentine','loveless'],['Tatsuro Yamashita','for you '],\n", 94 | " ['kano','kano'],['kraftwerk','trans-europe express'],['lambchop','nixon'],['land of the loops','bundle up joy'],['lcd sound system','sound of silver'],\n", 95 | " ['ramsey lewis','sun goddess'],['life without bindings','any other city'],['the lines ','hull down'],['prince and the revolution ','music from the motion picture purple rain'],\n", 96 | " ['t rex','the slider'],['the magnetic fields','holiday'],['mariah','utakana no hibi'],['gigi masin','wind'],['massive attack ','no protection '],\n", 97 | " ['midlake','the trials of van occupanther'],['charles mingus','mingus ah um'],['MR TC','Soundtrack for strangers'],\n", 98 | " ['The olivia tremor control','Music from the Unrealized Film Script: Dusk at Cubist Castle'],['the stone roses','the stone roses'],\n", 99 | " ['big star','#1 record'],['odd ','nosdam sisters'],['The other people place','lifestyles of the laptop café'],['Parquet Courts','Light up Gold'],\n", 100 | " ['The Psychedelic Furs','Talk Talk Talk'],['Pylon','Gyrate'],['Nelson Riddle','Changing Colors'],['Roxy Music','Avalon'],['Roykksopp and robin','do it again'],\n", 101 | " ['Talk Talk','Laughing stock'],['Placebo','1973'],['Saint Etienne','Foxbase alpha'],['Yasuaki Shimizu','Kakashi'],['Sloan','navy blues'],['Slowdive ','Souvlaki'],\n", 102 | " ['Spoon ','Gimme Fiction'],['Dusty Springfield','Dusty in Memphis'],['Stars of the Lid','Avec Laudenum'],['Cat Stevens','Teaser and the Firecat'],['Talking Heads ','Remain in Light'],\n", 103 | " ['Van Dyke Parks','Song Cycle'],['Al Stewart ','Year of the Cat'],['Gabor Szabo','Dreams'],['Tabos Project','Eyes of a Child'],\n", 104 | " ['Midori Takada','Through the looking glass'],['Masayoshi Takanaka','An insatiable high'],['Tame impala','Lonerism'],\n", 105 | " ['The 13th Floor Elevators','Easter Everywhere'],['Tin Pan Alley','Tin Pan Alley 2'],['Velvet Underground','White Light/White heat'],\n", 106 | " ['John Coltrane','My Favourite Things'],['Telstar','Sounds of the Tornados'],['Kurt Vile','Walking on a pretty daze'],['Scott Walker','Scott 4'],\n", 107 | " ['We Out Here','We out here'],['Wheedles Groove','Seattles Finest in Funk & Soul (1965-75)'],['The Who','Whos Next'],['Wilco ','being there '],\n", 108 | " ['XTC','Skylarking'],['X-Ray Specs','Germfree Adolescents'],['MFSB','Love is the Message'],['Yo La Tengo','I Can Hear the Heart Beating as One'],['Hiroshi Yoshimura','Pier & Loft'],\n", 109 | " ['You and I','You and I'],['Roland Young','Hearsay I-Land']]\n", 110 | "\n", 111 | "print(\" \".join([vinyl[i][1].split()[0] for i in range(9,105,10)]))" 112 | ] 113 | }, 114 | { 115 | "cell_type": "code", 116 | "execution_count": 6, 117 | "metadata": {}, 118 | "outputs": [ 119 | { 120 | "data": { 121 | "text/markdown": [ 122 | "### May 2021 : Solution\n", 123 | "\n", 124 | "With no clear instructions, this month’s puzzle was definitely on the harder side!\n", 125 | "Most of the records in the collection above are in alphabetical order, but not all. It turns out the records that are out of order are in positions 10, 11, 20, 21, 30, 31, …, 100, 101.\n", 126 | "The records in every 10th spot have each received a “perfect 10” rating from Pitchfork\n", 127 | "1\n", 128 | ".\n", 129 | "The records “past” these – i.e., the records in spots 11, 21, 31, …, 101, are:\n", 130 | "What’s\n", 131 | "Going On (by Marvin Gaye)\n", 132 | "The\n", 133 | "Dark Side of the Moon (Pink Floyd)\n", 134 | "Band\n", 135 | "on the Run (Paul McCartney & Wings)\n", 136 | "For\n", 137 | "You (Tatsuro Yamashita)\n", 138 | "The\n", 139 | "Slider (T. Rex)\n", 140 | "#1\n", 141 | "Record (Big Star)\n", 142 | "1973\n", 143 | "(Placebo)\n", 144 | "Song\n", 145 | "Cycle (Van Dyke Parks)\n", 146 | "My\n", 147 | "Favorite Things (John Coltrane)\n", 148 | "Love\n", 149 | "is the Message (MFSB)\n", 150 | "The first words of these “past tens” records form the question\n", 151 | "“What’s the band for the #1 1973 song ‘My Love’?”, and the answer is\n", 152 | "Paul McCartney\n", 153 | "2\n", 154 | "& Wings\n", 155 | ".\n", 156 | "[1] …those records being\n", 157 | "Low (David Bowie)\n", 158 | "On Fire (Galaxie 500)\n", 159 | "Unknown Pleasures (Joy Division)\n", 160 | "Loveless (My Bloody Valentine)\n", 161 | "Purple Rain (Prince)\n", 162 | "The Stone Roses\n", 163 | "Laughing Stock (Talk Talk)\n", 164 | "Remain in Light (Talking Heads)\n", 165 | "White Light/White Heat (The Velvet Underground)\n", 166 | "Germfree Adolescents (X-Ray Spex)\n", 167 | "[2] …who is alluded to with the knight on the lower-right corner and the\n", 168 | "die\n", 169 | "in the upper-left.\n", 170 | "Congrats to this month’s solvers who successfully decoded the question!" 171 | ], 172 | "text/plain": [ 173 | "" 174 | ] 175 | }, 176 | "metadata": {}, 177 | "output_type": "display_data" 178 | } 179 | ], 180 | "source": [ 181 | "url='https://www.janestreet.com/puzzles/past-tens-solution/'\n", 182 | "res = requests.get(url)\n", 183 | "soup = BeautifulSoup(res.content, 'html.parser')\n", 184 | "y =[text for text in soup.body.stripped_strings]\n", 185 | "#display([(i,j) for i,j in enumerate(y)])\n", 186 | "display(Markdown(\"### \"+y[8]+\"\\n\\n\"+str(\"\\n\".join(y[10:57]))))" 187 | ] 188 | }, 189 | { 190 | "cell_type": "code", 191 | "execution_count": null, 192 | "metadata": {}, 193 | "outputs": [], 194 | "source": [] 195 | } 196 | ], 197 | "metadata": { 198 | "colab": { 199 | "include_colab_link": true, 200 | "name": "JaneSt-Feb18.ipynb", 201 | "provenance": [] 202 | }, 203 | "kernelspec": { 204 | "display_name": "Python 3", 205 | "language": "python", 206 | "name": "python3" 207 | }, 208 | "language_info": { 209 | "codemirror_mode": { 210 | "name": "ipython", 211 | "version": 3 212 | }, 213 | "file_extension": ".py", 214 | "mimetype": "text/x-python", 215 | "name": "python", 216 | "nbconvert_exporter": "python", 217 | "pygments_lexer": "ipython3", 218 | "version": "3.7.10" 219 | } 220 | }, 221 | "nbformat": 4, 222 | "nbformat_minor": 4 223 | } 224 | -------------------------------------------------------------------------------- /2021_08_RobotTugOfWar(SageMath).ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 45, 6 | "metadata": { 7 | "collapsed": false, 8 | "jupyter": { 9 | "outputs_hidden": false 10 | } 11 | }, 12 | "outputs": [ 13 | { 14 | "name": "stdout", 15 | "output_type": "stream", 16 | "text": [ 17 | "piecewise(x|-->x on [0, 1/2); x)\n", 18 | "piecewise(x|-->1/2*x^2 + x + 1/2 on (-1, -1/2], x|-->1/2*x + 3/8 on (-1/2, 0], x|-->-1/2*x^2 + 1/2*x + 3/8 on (0, 1/2]; x)\n", 19 | "piecewise(x|-->1/4*x^2 + 1/4*x + 1/16 on (-1/2, 0], x|-->-1/6*x^3 + 1/4*x^2 + 1/4*x + 1/16 on (0, 1/2], x|-->-1/4*x^2 + 5/8*x - 1/48 on (1/2, 1], x|-->1/6*x^3 - 3/4*x^2 + 9/8*x - 3/16 on (1, 3/2]; x)\n", 20 | "piecewise(x|-->1/12*x^3 + 3/8*x^2 + 9/16*x + 9/32 on (-3/2, -1], x|-->-1/24*x^4 - 1/12*x^3 + 1/8*x^2 + 19/48*x + 23/96 on (-1, -1/2], x|-->-1/12*x^3 - 1/8*x^2 + 1/6*x + 71/384 on (-1/2, 0], x|-->1/24*x^4 - 1/12*x^3 - 1/8*x^2 + 1/6*x + 71/384 on (0, 1/2]; x)\n", 21 | "piecewise(x|-->-1/48*x^4 - 1/24*x^3 + 1/12*x^2 + 5/48*x + 7/256 on (-1/2, 0], x|-->1/120*x^5 - 1/48*x^4 - 1/24*x^3 + 1/12*x^2 + 5/48*x + 7/256 on (0, 1/2], x|-->1/48*x^4 - 1/24*x^3 - 1/12*x^2 + 97/384*x - 29/3840 on (1/2, 1], x|-->-1/120*x^5 + 1/16*x^4 - 1/8*x^3 + 27/128*x + 1/1280 on (1, 3/2]; x)\n", 22 | "piecewise(x|-->-1/240*x^5 - 1/32*x^4 - 1/18*x^3 + 1/32*x^2 + 39/256*x + 249/2560 on (-3/2, -1], x|-->1/720*x^6 + 1/240*x^5 - 1/96*x^4 - 1/36*x^3 + 5/96*x^2 + 617/3840*x + 2273/23040 on (-1, -1/2], x|-->1/240*x^5 + 1/96*x^4 - 1/36*x^3 - 5/96*x^2 + 1/15*x + 3521/46080 on (-1/2, 0], x|-->-1/720*x^6 + 1/240*x^5 + 1/96*x^4 - 1/36*x^3 - 5/96*x^2 + 1/15*x + 3521/46080 on (0, 1/2]; x)\n", 23 | "piecewise(x|-->1/1440*x^6 + 1/480*x^5 - 1/144*x^4 - 5/288*x^3 + 1/30*x^2 + 61/1440*x + 343/30720 on (-1/2, 0], x|-->-1/5040*x^7 + 1/1440*x^6 + 1/480*x^5 - 1/144*x^4 - 5/288*x^3 + 1/30*x^2 + 61/1440*x + 343/30720 on (0, 1/2], x|-->-1/1440*x^6 + 1/480*x^5 + 1/144*x^4 - 5/288*x^3 - 1/30*x^2 + 943/9216*x - 1933/645120 on (1/2, 1], x|-->1/5040*x^7 - 1/480*x^6 + 1/160*x^5 - 1/96*x^3 - 3/80*x^2 + 531/5120*x - 229/71680 on (1, 3/2]; x)\n", 24 | "piecewise(x|-->1/10080*x^7 + 1/960*x^6 + 1/360*x^5 - 1/384*x^4 - 7/720*x^3 + 7/320*x^2 + 669/10240*x + 1149/28672 on (-3/2, -1], x|-->-1/40320*x^8 - 1/10080*x^7 + 1/2880*x^6 + 1/720*x^5 - 5/1152*x^4 - 1/90*x^3 + 61/2880*x^2 + 42019/645120*x + 51673/1290240 on (-1, -1/2], x|-->-1/10080*x^7 - 1/2880*x^6 + 1/720*x^5 + 5/1152*x^4 - 1/90*x^3 - 61/2880*x^2 + 17/630*x + 35591/1146880 on (-1/2, 0], x|-->1/40320*x^8 - 1/10080*x^7 - 1/2880*x^6 + 1/720*x^5 + 5/1152*x^4 - 1/90*x^3 - 61/2880*x^2 + 17/630*x + 35591/1146880 on (0, 1/2]; x)\n", 25 | "piecewise(x|-->-1/80640*x^8 - 1/20160*x^7 + 1/4320*x^6 + 1/1152*x^5 - 1/360*x^4 - 61/8640*x^3 + 17/1260*x^2 + 277/16128*x + 56095/12386304 on (-1/2, 0], x|-->1/362880*x^9 - 1/80640*x^8 - 1/20160*x^7 + 1/4320*x^6 + 1/1152*x^5 - 1/360*x^4 - 61/8640*x^3 + 17/1260*x^2 + 277/16128*x + 56095/12386304 on (0, 1/2], x|-->1/80640*x^8 - 1/20160*x^7 - 1/4320*x^6 + 1/1152*x^5 + 1/360*x^4 - 61/8640*x^3 - 17/1260*x^2 + 428017/10321920*x - 225017/185794560 on (1/2, 1], x|-->-1/362880*x^9 + 1/26880*x^8 - 1/6720*x^7 + 1/1920*x^5 + 1/320*x^4 - 7/960*x^3 - 3/224*x^2 + 47529/1146880*x - 1663/1376256 on (1, 3/2]; x)\n", 26 | "piecewise(x|-->-1/725760*x^9 - 1/53760*x^8 - 1/15120*x^7 + 1/11520*x^6 + 7/14400*x^5 - 7/3840*x^4 - 137/30240*x^3 + 461/53760*x^2 + 12109/458752*x + 372367/22937600 on (-3/2, -1], x|-->1/3628800*x^10 + 1/725760*x^9 - 1/161280*x^8 - 1/30240*x^7 + 1/6912*x^6 + 1/1800*x^5 - 61/34560*x^4 - 17/3780*x^3 + 277/32256*x^2 + 4904657/185794560*x + 30162239/1857945600 on (-1, -1/2], x|-->1/725760*x^9 + 1/161280*x^8 - 1/30240*x^7 - 1/6912*x^6 + 1/1800*x^5 + 61/34560*x^4 - 17/3780*x^3 - 277/32256*x^2 + 31/2835*x + 6678007/530841600 on (-1/2, 0], x|-->-1/3628800*x^10 + 1/725760*x^9 + 1/161280*x^8 - 1/30240*x^7 - 1/6912*x^6 + 1/1800*x^5 + 61/34560*x^4 - 17/3780*x^3 - 277/32256*x^2 + 31/2835*x + 6678007/530841600 on (0, 1/2]; x)\n", 27 | "piecewise(x|-->1/7257600*x^10 + 1/1451520*x^9 - 1/241920*x^8 - 1/48384*x^7 + 1/10800*x^6 + 61/172800*x^5 - 17/15120*x^4 - 277/96768*x^3 + 31/5670*x^2 + 50521/7257600*x + 649613/353894400 on (-1/2, 0], x|-->-1/39916800*x^11 + 1/7257600*x^10 + 1/1451520*x^9 - 1/241920*x^8 - 1/48384*x^7 + 1/10800*x^6 + 61/172800*x^5 - 17/15120*x^4 - 277/96768*x^3 + 31/5670*x^2 + 50521/7257600*x + 649613/353894400 on (0, 1/2], x|-->-1/7257600*x^10 + 1/1451520*x^9 + 1/241920*x^8 - 1/48384*x^7 - 1/10800*x^6 + 61/172800*x^5 + 17/15120*x^4 - 277/96768*x^3 - 31/5670*x^2 + 2497931/148635648*x - 1146107/2335703040 on (1/2, 1], x|-->1/39916800*x^11 - 1/2419200*x^10 + 1/483840*x^9 - 1/80640*x^7 - 1/9600*x^6 + 7/19200*x^5 + 1/896*x^4 - 461/161280*x^3 - 7/1280*x^2 + 770979/45875200*x - 13371931/27249868800 on (1, 3/2]; x)\n" 28 | ] 29 | } 30 | ], 31 | "source": [ 32 | "y = var('y')\n", 33 | "assume(y>-1/2)\n", 34 | "assume(y<1/2)\n", 35 | "assume(y,'real')\n", 36 | "\n", 37 | "y=0\n", 38 | "\n", 39 | "x = PolynomialRing(QQ,'x')\n", 40 | "a = piecewise([[[0,1],1]])\n", 41 | "b = piecewise([[[-1,0],1]])\n", 42 | "\n", 43 | "\n", 44 | "r = piecewise([[[-y,1-y],1]])\n", 45 | "r = r.restriction([-1/2,1/2])\n", 46 | "\n", 47 | "print(r.integral())\n", 48 | "\n", 49 | "for i in range(5):\n", 50 | " r=r.convolution(b)\n", 51 | " print(r.integral())\n", 52 | " r=r.restriction([-1/2,1/2])\n", 53 | " \n", 54 | " r=r.convolution(a)\n", 55 | " print(r.integral())\n", 56 | " r=r.restriction([-1/2,1/2])\n" 57 | ] 58 | }, 59 | { 60 | "cell_type": "code", 61 | "execution_count": 28, 62 | "metadata": { 63 | "collapsed": false, 64 | "jupyter": { 65 | "outputs_hidden": false 66 | } 67 | }, 68 | "outputs": [], 69 | "source": [ 70 | "def ans(y):\n", 71 | " x = PolynomialRing(QQ,'x').gen()\n", 72 | " a = piecewise([[[0,1],1]],var=x)\n", 73 | " b = piecewise([[[-1,0],1]],var=x)\n", 74 | " a_tot = 0\n", 75 | "\n", 76 | " r = piecewise([[[-1,1],1]],var=x)\n", 77 | " r = r.restriction([-y,1-y])\n", 78 | "\n", 79 | " res = 1-r.integral()(x=1/2)\n", 80 | " a_tot += res\n", 81 | " r=r.restriction([-1/2,1/2])\n", 82 | "\n", 83 | " while r.integral()(x=r.end_points()[-1]-0.0000000001) > 1e-8:\n", 84 | " r=r.convolution(b)\n", 85 | " res = r.integral()(x=-1/2)\n", 86 | " r=r.restriction([-1/2,1/2])\n", 87 | "\n", 88 | " r=r.convolution(a)\n", 89 | " res = r.integral()(x=3/2)-r.integral()(x=1/2)\n", 90 | " a_tot += res\n", 91 | " r=r.restriction([-1/2,1/2])\n", 92 | " \n", 93 | " return a_tot\n", 94 | " " 95 | ] 96 | }, 97 | { 98 | "cell_type": "code", 99 | "execution_count": 29, 100 | "metadata": { 101 | "collapsed": false, 102 | "jupyter": { 103 | "outputs_hidden": false 104 | } 105 | }, 106 | "outputs": [ 107 | { 108 | "name": "stdout", 109 | "output_type": "stream", 110 | "text": [ 111 | "0.285000000000000 0.500000109635224\n", 112 | "0.285000100000000 0.500000018197366\n", 113 | "0.285000200000000 0.4999999267595027\n" 114 | ] 115 | } 116 | ], 117 | "source": [ 118 | "y = 0.2849999\n", 119 | "for i in range(3):\n", 120 | " y += 0.0000001\n", 121 | " print(y,ans(y))" 122 | ] 123 | }, 124 | { 125 | "cell_type": "code", 126 | "execution_count": null, 127 | "metadata": { 128 | "collapsed": false, 129 | "jupyter": { 130 | "outputs_hidden": false 131 | } 132 | }, 133 | "outputs": [], 134 | "source": [] 135 | } 136 | ], 137 | "metadata": { 138 | "kernelspec": { 139 | "display_name": "Python 3", 140 | "language": "python", 141 | "name": "python3" 142 | }, 143 | "language_info": { 144 | "codemirror_mode": { 145 | "name": "ipython", 146 | "version": 3 147 | }, 148 | "file_extension": ".py", 149 | "mimetype": "text/x-python", 150 | "name": "python", 151 | "nbconvert_exporter": "python", 152 | "pygments_lexer": "ipython3", 153 | "version": "3.7.10" 154 | } 155 | }, 156 | "nbformat": 4, 157 | "nbformat_minor": 4 158 | } 159 | -------------------------------------------------------------------------------- /2021_10_RobotSwimmingTrials.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": { 7 | "id": "Oq1xibzMT1Wl" 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "import time\n", 12 | "import requests\n", 13 | "from bs4 import BeautifulSoup\n", 14 | "from IPython.display import Markdown, display,HTML\n", 15 | "from random import randrange\n", 16 | "from scipy.special import comb\n", 17 | "import numpy as np\n", 18 | "import numba as nb" 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": 2, 24 | "metadata": { 25 | "id": "ICTfvjZUT1Wr" 26 | }, 27 | "outputs": [ 28 | { 29 | "data": { 30 | "text/html": [ 31 | "
\n", 32 | "

As we have twice before, we find ourselves engrossed in a robot sports competition. In the Robot Swimming Trials, 3N identical robots compete for N equivalent spots in the finals by swimming N races. Each robot precommits to spending a certain amount of its fuel in each race. After all the races are run, the spots in the finals are given to the winners of the races, moving from the fastest winner to the slowest. (Once a robot wins a race, it is ineligible to win another race.) A robot’s speed is strictly increasing in the amount of fuel it spends, and ties are broken by randomly choosing the winner among the robots that have spent the same amount of fuel.

\n", 33 | "

Mathematically speaking, the 3N robots each submit a strategy, which is an N-tuple of nonnegative real number “bids” summing to 1, representing the fuel burned in each of the N races. The winners of the races are then determined from the highest bid (across all races and all robots) on down, with ties broken randomly. Once a robot wins a race their other bids are deleted, so we are guaranteed to get N distinct qualifiers for the finals.

\n", 34 | "

For example, suppose N=3 and the 3N=9 robots submit their strategies as

\n", 35 | "\n", 36 | "\n", 37 | "\n", 38 | "\n", 39 | "\n", 40 | "\n", 41 | "\n", 42 | "\n", 43 | "\n", 44 | "\n", 45 | "\n", 46 | "\n", 47 | "\n", 48 | "\n", 49 | "\n", 50 | "\n", 51 | "\n", 52 | "\n", 53 | "\n", 54 | "\n", 55 | "\n", 56 | "\n", 57 | "\n", 58 | "\n", 59 | "\n", 60 | "\n", 61 | "\n", 62 | "\n", 63 | "\n", 64 | "\n", 65 | "\n", 66 | "\n", 67 | "\n", 68 | "\n", 69 | "\n", 70 | "\n", 71 | "\n", 72 | "\n", 73 | "\n", 74 | "\n", 75 | "\n", 76 | "\n", 77 | "\n", 78 | "\n", 79 | "\n", 80 | "\n", 81 | "\n", 82 | "\n", 83 | "\n", 84 | "\n", 85 | "\n", 86 | "\n", 87 | "\n", 88 | "\n", 89 | "\n", 90 | "\n", 91 | "\n", 92 | "\n", 93 | "\n", 94 | "\n", 95 | "\n", 96 | "\n", 97 | "\n", 98 | "\n", 99 | "\n", 100 | "
RobotRace 1  Race 2  Race 3  
Automatonya  0.60.10.3
Botty0.60.30.1
Chroma010
Data0.30.50.2
Electro0.20.80
Fernandroid0.40.50.1
Gregulator0.50.50
Hannanobot00.90.1
IO0.20.70.1
\n", 101 | "

The second race gets resolved first because Chroma’s bid of 1 is the highest overall, and Chroma is declared the winner of that time trial. Next, the first race is resolved because 0.6 is the highest remaining bid (we ignore the 0.9, 0.8, and 0.7 in the second race because it already has a winner). We flip a fair coin to determine who is the winner between Automatonya and Botty; say that Automatonya gets lucky and is declared the winner. Then the third race is decided, and Data is declared the winner, because 0.2 is the highest bid among robots that have not yet won (Automatonya’s 0.3 is ignored).

\n", 102 | "

Over the storied history of the RST, the metagame settled into what was widely believed to be the Nash equilibrium: each robot uniformly randomly selects a race and devotes all of their fuel to it. Let’s call this the discrete strategy. However, rumors are circulating that this conventional wisdom is not entirely accurate: for a large enough N, the discrete strategy is not the Nash equilibrium. You’ve been tasked to find two pieces of information:

\n", 103 | "

What is the smallest N for which the trial does not have the discrete strategy as the Nash equilibrium?

\n", 104 | "

For this N, if the other 3N-1 robots naively play the discrete\n", 105 | "strategy and your robot plays optimally (exploiting this knowledge of\n", 106 | "your opponents’ strategies), with what probability p will you make the finals (rounded to 6 significant digits)?

\n", 107 | "

Please submit your answer in the form “N, p”.

\n", 108 | "
" 109 | ], 110 | "text/plain": [ 111 | "" 112 | ] 113 | }, 114 | "execution_count": 2, 115 | "metadata": {}, 116 | "output_type": "execute_result" 117 | } 118 | ], 119 | "source": [ 120 | "url='https://www.janestreet.com/puzzles/robot-swimming-trials-index/'\n", 121 | "res = requests.get(url)\n", 122 | "soup = BeautifulSoup(res.content, 'html.parser')\n", 123 | "HTML(str(soup.find('div', {'class' :'inner-wrapper'})))" 124 | ] 125 | }, 126 | { 127 | "cell_type": "code", 128 | "execution_count": 3, 129 | "metadata": { 130 | "id": "844RhDFpT1Wu" 131 | }, 132 | "outputs": [], 133 | "source": [ 134 | "# A some point there is a larger than 1/3 probability of there being at least one race where every other robot choses zero. \n", 135 | "# So if the last robot spreads evenly over the other races he will win any race where there are no other robots who choose 1.\n", 136 | "# The probability of throwing N balls into k bins and there being a ball in every bin is as per the link below.\n", 137 | "# https://math.stackexchange.com/questions/174674/if-n-balls-are-thrown-into-k-bins-what-is-the-probability-that-every-bin-gets-a" 138 | ] 139 | }, 140 | { 141 | "cell_type": "markdown", 142 | "metadata": { 143 | "id": "844RhDFpT1Wu" 144 | }, 145 | "source": [ 146 | "$P(X=0)=\\sum_{j=0}^k (-1)^j {k\\choose j}\\left(1-{j\\over k}\\right)^n$" 147 | ] 148 | }, 149 | { 150 | "cell_type": "code", 151 | "execution_count": 4, 152 | "metadata": { 153 | "id": "844RhDFpT1Wu" 154 | }, 155 | "outputs": [ 156 | { 157 | "name": "stdout", 158 | "output_type": "stream", 159 | "text": [ 160 | "For 8 rounds the chance is 0.334578 \n" 161 | ] 162 | } 163 | ], 164 | "source": [ 165 | "def calc(k):\n", 166 | " n = 3* k - 1\n", 167 | " return 1-sum([pow(-1,h)*comb(k,h)*(1-h/k)**n for h in range(k)])\n", 168 | "\n", 169 | "flag = 0\n", 170 | "i=1\n", 171 | "while flag==0:\n", 172 | " i+=1\n", 173 | " n=i*3 \n", 174 | " if calc(i) > 1/3:\n", 175 | " print(\"For {} rounds the chance is {:.6f} \".format(i,calc(i)))\n", 176 | " flag=1" 177 | ] 178 | }, 179 | { 180 | "cell_type": "code", 181 | "execution_count": 5, 182 | "metadata": {}, 183 | "outputs": [ 184 | { 185 | "data": { 186 | "text/html": [ 187 | "
\n", 188 | "

We are given that our opponents are all playing the discrete strategy,\n", 189 | "which means they will all be independently choosing a race uniformly\n", 190 | "randomly and devoting\n", 191 | "all of their fuel to it. Since we are searching for the smallest N\n", 192 | "so that the discrete strategy is not optimal, we will be playing\n", 193 | "something other than this strategy. This means we won’t be assigning\n", 194 | "all of our fuel to any one race, but since all of our opponents are\n", 195 | "“bidding” 1 or 0 on every race, the best non-discrete strategies will\n", 196 | "involve bidding a nonzero amount on every race, which will beat all of\n", 197 | "the 0 bids (but lose to all of the 1 bids). So without loss of\n", 198 | "generality we can assume the strategy of bidding 1/N on every race,\n", 199 | "we can call this the uniform strategy.

\n", 200 | "

Now we need to compute the smallest N for which this beats out the\n", 201 | "discrete strategy. The uniform strategy wins a race exactly when none\n", 202 | "of the 3N-1 other discrete-strategy-playing robots select that race\n", 203 | "for their fuel. It’s straightforward to compute this probability for a\n", 204 | "single race, (1-1/N)^(3N-1). But the assignment of discrete\n", 205 | "strategies is not independent across the N races! Assuming they were\n", 206 | "would give an incorrect answer of N=9 and p=0.350245…

\n", 207 | "

Also, these events of winning a race with the uniform strategy are\n", 208 | "not disjoint across the N races! Assuming they were would give\n", 209 | "an incorrect answer of N=8 and p=0.370916…

\n", 210 | "

Instead, a nice recursion could be found to count up the number of\n", 211 | "arrangements of the 3N-1 other players that left at least one race\n", 212 | "undefended. For example, let P(R,m,n) equal the probability that, if we\n", 213 | "need to assign R robots to (m+n) total races, m of which already have\n", 214 | "a robot assigned and n of which don’t, we will eventually assign at\n", 215 | "least one robot to all (m+n) races. Then assigning the next robot to a\n", 216 | "race uniformly randomly implies the recursion:

\n", 217 | "

P(R,m,n) = (m * P(R-1,m,n) + n * P(R-1,m+1,n-1))/(m+n).

\n", 218 | "

Along with the boundary values

\n", 219 | "

P(R,m,0) = 1 (all races have been assigned at least one robot)

\n", 220 | "

and for n>0,

\n", 221 | "

P(0,m,n) = 0 (we’ve run out of robots and we still have an unassigned\n", 222 | "race),

\n", 223 | "

we can compute values of P efficiently by induction. We want to find\n", 224 | "the smallest N such that

\n", 225 | "

1 - P(3N-1,0,N) > 1/3,

\n", 226 | "

which occurs at N=8 and p=0.334578….

\n", 227 | "

Congrats to this month’s solvers who computed the correct size and\n", 228 | "probability of winning the swimming trials!

\n", 229 | "

\n", 230 | "

\n", 231 | "
" 232 | ], 233 | "text/plain": [ 234 | "" 235 | ] 236 | }, 237 | "execution_count": 5, 238 | "metadata": {}, 239 | "output_type": "execute_result" 240 | } 241 | ], 242 | "source": [ 243 | "url='https://www.janestreet.com/puzzles/robot-swimming-trials-solution/'\n", 244 | "res = requests.get(url)\n", 245 | "soup = BeautifulSoup(res.content, 'html.parser')\n", 246 | "HTML(str(soup.find('div', {'class' :'inner-wrapper'})))" 247 | ] 248 | }, 249 | { 250 | "cell_type": "code", 251 | "execution_count": 6, 252 | "metadata": {}, 253 | "outputs": [], 254 | "source": [ 255 | "# Check with some simulations\n", 256 | "@nb.njit()\n", 257 | "def sim(N):\n", 258 | " picks = [randrange(1, N+1) for p in range(N*3-1)]\n", 259 | " for n in range(1,N+1):\n", 260 | " if n not in picks:\n", 261 | " return 1\n", 262 | " return 0\n", 263 | "\n", 264 | "@nb.njit()\n", 265 | "def run_it(N,sims):\n", 266 | " tot = 0\n", 267 | " for x in range(sims):\n", 268 | " tot += sim(N)\n", 269 | " return tot/sims\n" 270 | ] 271 | }, 272 | { 273 | "cell_type": "code", 274 | "execution_count": 7, 275 | "metadata": {}, 276 | "outputs": [ 277 | { 278 | "name": "stdout", 279 | "output_type": "stream", 280 | "text": [ 281 | "Rounds\tSim\t\tCalc\n", 282 | " 2\t0.062528\t0.062500\n", 283 | " 3\t0.116596\t0.116598\n", 284 | " 4\t0.166131\t0.166012\n", 285 | " 5\t0.212186\t0.212093\n", 286 | " 6\t0.255354\t0.255368\n", 287 | " 7\t0.296034\t0.296128\n", 288 | " 8\t0.334598\t0.334578\n", 289 | " 9\t0.370773\t0.370878\n", 290 | "took 52.159483194351196\n" 291 | ] 292 | } 293 | ], 294 | "source": [ 295 | "start= time.time()\n", 296 | "print(\"Rounds\\tSim\\t\\tCalc\")\n", 297 | "for N in range(2,10):\n", 298 | " sims = 10000000\n", 299 | " x = run_it(N,sims)\n", 300 | " print(\"{:>3}\\t{:.6f}\\t{:.6f}\".format(N,x,calc(N)))\n", 301 | "print(\"took\",time.time()-start)" 302 | ] 303 | }, 304 | { 305 | "cell_type": "code", 306 | "execution_count": null, 307 | "metadata": {}, 308 | "outputs": [], 309 | "source": [] 310 | } 311 | ], 312 | "metadata": { 313 | "colab": { 314 | "include_colab_link": true, 315 | "name": "JaneSt-Feb18.ipynb", 316 | "provenance": [] 317 | }, 318 | "kernelspec": { 319 | "display_name": "Python 3 (ipykernel)", 320 | "language": "python", 321 | "name": "python3" 322 | }, 323 | "language_info": { 324 | "codemirror_mode": { 325 | "name": "ipython", 326 | "version": 3 327 | }, 328 | "file_extension": ".py", 329 | "mimetype": "text/x-python", 330 | "name": "python", 331 | "nbconvert_exporter": "python", 332 | "pygments_lexer": "ipython3", 333 | "version": "3.7.12" 334 | } 335 | }, 336 | "nbformat": 4, 337 | "nbformat_minor": 4 338 | } 339 | -------------------------------------------------------------------------------- /2024_04_RobotCaptureTheFlag.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "id": "46823cb5-38bc-44d0-8cd7-22b98fcb380b", 7 | "metadata": {}, 8 | "outputs": [], 9 | "source": [ 10 | "import numpy as np\n", 11 | "import matplotlib.pyplot as plt\n", 12 | "import requests\n", 13 | "from bs4 import BeautifulSoup\n", 14 | "from IPython.display import Markdown, display,IFrame,HTML,Image\n", 15 | "\n", 16 | "from scipy.optimize import minimize\n", 17 | "from scipy.integrate import quad" 18 | ] 19 | }, 20 | { 21 | "cell_type": "code", 22 | "execution_count": 2, 23 | "id": "7cc3a8b7-12ee-4ac5-b074-ffe705030f4d", 24 | "metadata": {}, 25 | "outputs": [ 26 | { 27 | "data": { 28 | "text/markdown": [ 29 | "### Robot Capture-the-Flag\n", 30 | "It’s been a while and change, but the\n", 31 | "Robot\n", 32 | "Games\n", 33 | "are back once again. This time it’s\n", 34 | "Capture the Flag\n", 35 | "!\n", 36 | "Two robots, Aaron and Erin, have made it to this year’s final! Initially they are situated at the center of a unit circle. A\n", 37 | "flag\n", 38 | "is placed somewhere inside the circle, at a location chosen uniformly at random. Once the flag is placed, Aaron is able to deduce its\n", 39 | "distance\n", 40 | "to the flag, and Erin is only able to deduce its\n", 41 | "direction\n", 42 | "to the flag. (Equivalently: if (\n", 43 | "r\n", 44 | ",\n", 45 | "θ\n", 46 | ") are the polar coordinates of the flag’s location, Aaron is told\n", 47 | "r\n", 48 | "and Erin is told\n", 49 | "θ\n", 50 | ".)\n", 51 | "Both robots are allowed to make a\n", 52 | "single move\n", 53 | "after the flag is placed, if they wish. Any move they make is without knowledge of what the other robot is doing. (And they may not move outside the circle.)\n", 54 | "Whichever robot is\n", 55 | "closer\n", 56 | "to the flag after these moves\n", 57 | "captures\n", 58 | "the flag and is declared the winner!\n", 59 | "During the preliminaries it was discovered that Erin is programmed to play a fixed distance along the detected angle\n", 60 | "θ" 61 | ], 62 | "text/plain": [ 63 | "" 64 | ] 65 | }, 66 | "metadata": {}, 67 | "output_type": "display_data" 68 | } 69 | ], 70 | "source": [ 71 | "url='https://www.janestreet.com/puzzles/robot-capture-the-flag-index/'\n", 72 | "res = requests.get(url)\n", 73 | "soup = BeautifulSoup(res.content, 'html.parser')\n", 74 | "y =[text.replace(\">\",\"greater than\") for text in soup.body.stripped_strings]\n", 75 | "\n", 76 | "#display([(i,j) for i,j in enumerate(y)])\n", 77 | "display(Markdown(\"### \"+y[13]+\"\\n\"+\"\\n\".join(y[16:47])))" 78 | ] 79 | }, 80 | { 81 | "cell_type": "code", 82 | "execution_count": 3, 83 | "id": "96873de6-5ed6-4105-a852-1e23de568c31", 84 | "metadata": { 85 | "tags": [] 86 | }, 87 | "outputs": [ 88 | { 89 | "data": { 90 | "text/markdown": [ 91 | "## Erin's best strategy is to jump *0.5013064106* giving Aaron a *0.1661864865* chance of winning" 92 | ], 93 | "text/plain": [ 94 | "" 95 | ] 96 | }, 97 | "metadata": {}, 98 | "output_type": "display_data" 99 | } 100 | ], 101 | "source": [ 102 | "# Erin strategy => jump x in direction of theta\n", 103 | "# Aaron strategy stay at center if r < x/2 otherwise jump a distance y that maximises the chance of winning \n", 104 | "# Erin minimises the chance of an aaron win\n", 105 | "\n", 106 | "theta = lambda r, x: np.arcsin(np.abs(r-x)/r)/np.pi * 2 * r \n", 107 | "prob_aron_win = lambda x: (x/2)**2 + quad(theta, x/2, 1,args=(x,))[0] \n", 108 | "\n", 109 | "solved = minimize(prob_aron_win, x0=0.5, bounds=[(0,1)])\n", 110 | "\n", 111 | "display(Markdown(\"## Erin's best strategy is to jump *{:.10f}* giving Aaron a *{:.10f}* chance of winning\".format(solved['x'][0],solved['fun'])))" 112 | ] 113 | }, 114 | { 115 | "cell_type": "code", 116 | "execution_count": 4, 117 | "id": "97a139aa-a991-43e1-be1a-3776762bc15b", 118 | "metadata": {}, 119 | "outputs": [ 120 | { 121 | "data": { 122 | "text/markdown": [ 123 | "### Robot Capture-the-Flag\n", 124 | "Aaron has learned that Erin always plays a fixed distance — call it\n", 125 | "e\n", 126 | "— along the correct angle. Erin knows that Aaron knows this, and that Aaron will pick a distance\n", 127 | "a\n", 128 | "(and an angle at random, presumably) to maximize\n", 129 | "P\n", 130 | "(Aaron wins). So Erin should pick a value for\n", 131 | "e\n", 132 | "that minimizes this probability.\n", 133 | "So we need to determine\n", 134 | "P\n", 135 | "(Aaron wins) for given values of\n", 136 | "e\n", 137 | "and\n", 138 | "r\n", 139 | ". If\n", 140 | "r\n", 141 | "is less than\n", 142 | "e\n", 143 | "/2, Aaron should choose to stay at the center, thus guaranteeing a win. Otherwise, Aaron should pick the distance\n", 144 | "a\n", 145 | "that maximizes the\n", 146 | "fraction\n", 147 | "of the circle of radius\n", 148 | "a\n", 149 | "located within a distance of |\n", 150 | "r\n", 151 | "−\n", 152 | "e\n", 153 | "| of the flag.\n", 154 | "Maximizing the fraction of the circle of radius" 155 | ], 156 | "text/plain": [ 157 | "" 158 | ] 159 | }, 160 | "metadata": {}, 161 | "output_type": "display_data" 162 | } 163 | ], 164 | "source": [ 165 | "url='https://www.janestreet.com/puzzles/robot-capture-the-flag-solution/'\n", 166 | "res = requests.get(url)\n", 167 | "soup = BeautifulSoup(res.content, 'html.parser')\n", 168 | "y =[text for text in soup.body.stripped_strings]\n", 169 | "\n", 170 | "#display([(i,j) for i,j in enumerate(y)])\n", 171 | "display(Markdown(\"### \"+y[13]+\"\\n\"+\"\\n\".join(y[16:47])))" 172 | ] 173 | }, 174 | { 175 | "cell_type": "code", 176 | "execution_count": null, 177 | "id": "35092b48-b04c-49c1-861d-e91b56994be7", 178 | "metadata": {}, 179 | "outputs": [], 180 | "source": [] 181 | } 182 | ], 183 | "metadata": { 184 | "kernelspec": { 185 | "display_name": "Python 3 (ipykernel)", 186 | "language": "python", 187 | "name": "python3" 188 | }, 189 | "language_info": { 190 | "codemirror_mode": { 191 | "name": "ipython", 192 | "version": 3 193 | }, 194 | "file_extension": ".py", 195 | "mimetype": "text/x-python", 196 | "name": "python", 197 | "nbconvert_exporter": "python", 198 | "pygments_lexer": "ipython3", 199 | "version": "3.9.12" 200 | } 201 | }, 202 | "nbformat": 4, 203 | "nbformat_minor": 5 204 | } 205 | -------------------------------------------------------------------------------- /Path.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gowen100/Jane-Street-Solutions/d4c464522085e10b459851623e3632023b6239c0/Path.jpg -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **Found out about these puzzles from this efinancialcareers article from July. Going back and looking at some of the older ones when I have time.** 2 | 3 | The puzzle site is here https://www.janestreet.com/puzzles/current-puzzle/ 4 | 5 | A good tableau based league table is here 6 | https://public.tableau.com/app/profile/heidi.stockton/viz/PuzzlesofJaneStreet/JaneStreet 7 |
8 | 9 | 10 | -------------------------------------------------------------------------------- /Semaphore.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gowen100/Jane-Street-Solutions/d4c464522085e10b459851623e3632023b6239c0/Semaphore.jpg -------------------------------------------------------------------------------- /cbanana_raw.txt: -------------------------------------------------------------------------------- 1 | pzprv3 2 | cbanana 3 | 12 4 | 20 5 | 6 6 . . . . . . . . . . . . . . . . 6 6 6 | 6 . . . . . . . . 8 12 . . . . . . . . 6 7 | . . . 10 10 . . . . . . . . . . 12 12 . . . 8 | . . . 10 . . 10 10 . . . . 11 11 . . 4 . . . 9 | . . . . . . 10 . . . . . . 11 . . . . . . 10 | . 15 . . . . . . . 3 4 . . . . . . . 3 . 11 | . 4 . . . . . . . 6 5 . . . . . . . 12 . 12 | . . . . . . 9 . . . . . . 8 . . . . . . 13 | . . . 15 . . 9 9 . . . . 8 8 . . 8 . . . 14 | . . . 1 9 . . . . . . . . . . 1 7 . . . 15 | 4 . . . . . . . . 12 8 . . . . . . . . 4 16 | 4 4 . . . . . . . . . . . . . . . . 4 4 17 | . . . . . . . . . . . . . . . . . . . . 18 | . . . . . . . . . . . . . . . . . . . . 19 | . . . . . . . . . . . . . . . . . . . . 20 | . . . . . . . . . . . . . . . . . . . . 21 | . . . . . . . . . . . . . . . . . . . . 22 | . . . . . . . . . . . . . . . . . . . . 23 | . . . . . . . . . . . . . . . . . . . . 24 | . . . . . . . . . . . . . . . . . . . . 25 | . . . . . . . . . . . . . . . . . . . . 26 | . . . . . . . . . . . . . . . . . . . . 27 | . . . . . . . . . . . . . . . . . . . . 28 | . . . . . . . . . . . . . . . . . . . . 29 | -------------------------------------------------------------------------------- /cbanana_solved.txt: -------------------------------------------------------------------------------- 1 | pzprv3 2 | cbanana 3 | 12 4 | 20 5 | 6 6 . . . . . . . . . . . . . . . . 6 6 6 | 6 . . . . . . . . 8 12 . . . . . . . . 6 7 | . . . 10 10 . . . . . . . . . . 12 12 . . . 8 | . . . 10 . . 10 10 . . . . 11 11 . . 4 . . . 9 | . . . . . . 10 . . . . . . 11 . . . . . . 10 | . 15 . . . . . . . 3 4 . . . . . . . 3 . 11 | . 4 . . . . . . . 6 5 . . . . . . . 12 . 12 | . . . . . . 9 . . . . . . 8 . . . . . . 13 | . . . 15 . . 9 9 . . . . 8 8 . . 8 . . . 14 | . . . 1 9 . . . . . . . . . . 1 7 . . . 15 | 4 . . . . . . . . 12 8 . . . . . . . . 4 16 | 4 4 . . . . . . . . . . . . . . . . 4 4 17 | + + + # + + + + + # + + + + + + + # # # 18 | + + # + # # # # + + # # # # # # + # # # 19 | + # + + + + + + # + # # # # # # + + + + 20 | # + # # # # # + + # + + + + + + # # # # 21 | + + # # # # # + # + # # + + + # + + + + 22 | + + + + + + + # + + # # + # + # + # # # 23 | # # + + # # # + # # + + # + # + + + + + 24 | # # + + # # # + # # + + # + # + # # # # 25 | + + # + # # # + # # + # + + # + # # # # 26 | + + + # + + + + + + # + # + + # + + + + 27 | # # + + # # # # # # + + + # + + # + # # 28 | # # + + # # # # # # + + + + # # + + # # 29 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | numpy 2 | scipy 3 | sympy 4 | seaborn 5 | z3-solver 6 | numba 7 | bs4 8 | pulp 9 | rdkit-pypi 10 | networkx 11 | scikit-image 12 | shapely --------------------------------------------------------------------------------