{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "### Knight Moves 6\n", "\n", "[![](https://www.janestreet.com/puzzles/october-2024.png)](https://www.janestreet.com/puzzles/october-2024.png)\n", "\n", "Pick **distinct positive integers** _A_, _B_, and _C_, and place them in the grid above. Your goal is to create two corner-to-corner trips — one from _a1_ to _f6_, and the other from _a6_ to _f1_ — both of which score **exactly 2024 points**.\n", "\n", "A “trip” consists of knight’s moves. Squares may **not** be revisited within a trip.\n", "\n", "The “score” for a trip is calculated as follows:\n", "\n", "- Start with _A_ points.\n", "- Every time you make a move:\n", " - if your move is between two _different_ integers, **multiply** your score by the value you are moving to;\n", " - otherwise, **increment** your score by the value you are moving to.\n", "\n", "Can you find positive integers _A_, _B_, and _C_, as well as a pair of trips, that satisfy the criteria above? How low can you get _A_ + _B_ + _C_?\n", "\n", "Please format your entry by concatenating your values for _A_, _B_, and _C_, followed by your _a1_-to-_f6_ tour, followed by your _a6_-to-_f1_ tour. For example, “1,2,253,a1,b3,c5,d3,f4,d5,f6,a6,c5,a4,b2,c4,d2,f1” would be a properly formatted entry.\n", "\n", "To qualify for the leaderboard your value for _A_ + _B_ + _C_ must be **less than 50**." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "#code\n", "\n", "from IPython.core.interactiveshell import InteractiveShell\n", "InteractiveShell.ast_node_interactivity = \"all\"\n", "\n", "\n", "import numpy as np\n", "import pandas as pd\n", "\n", "\n", "def encode_scoregrid(path:str):\n", " return { (j,i):x+str(y) for i,x in enumerate(['a','b','c','d','e','f']) for j,y in enumerate([1,2,3,4,5,6][::-1])}\n", "\n", "def decode_scoregrid():\n", " return { x+str(y):(j,i) for i,x in enumerate(['a','b','c','d','e','f']) for j,y in enumerate([1,2,3,4,5,6][::-1])}\n", "\n", "def calc_score_tuple(tuplepaths,scoregrid):\n", " \n", " prevpos=None\n", " score=None\n", " for i,pos in enumerate(tuplepaths):\n", " \n", " if i==0:\n", " prevpos=pos\n", " score=scoregrid[pos]\n", " elif scoregrid[prevpos]==scoregrid[pos]:\n", " score+=scoregrid[pos]\n", " prevpos=pos\n", " else:\n", " score*=scoregrid[pos]\n", " prevpos=pos\n", "\n", " return score\n", " \n", "def calc_score_str(path,scoregrid:str):\n", " decode=decode_scoregrid()\n", " path = path.split(',')\n", "\n", " return calc_score_tuple([decode[x] for x in path],scoregrid)\n", " \n", " \n", " \n", "\n", "class KnightTour:\n", "\n", " def __init__(self,pos,prevpos,posstr,score,tours,nmoves):\n", "\n", " \n", " self.currpos=pos\n", " self.prevpos= prevpos\n", " self.curposstr=posstr\n", " self.currscore=score\n", " self.tours=tours\n", " self.nummoves=nmoves\n", " \n", "\n", "\n", " def __repr__(self):\n", " return f'tour={self.tours}, score={self.currscore}'\n", "\n", "class Grid:\n", "\n", " def __init__(self,dims=(6,6)):\n", " \n", "\n", " xlabel_i2key = {0:'a',1:'b',2:'c',3:'d',4:'e',5:'f'}\n", " ylabel_i2key = {0:6,1:5,2:4,3:3,4:2,5:1}\n", " xlabel_key2i ={v:k for k,v in xlabel_i2key.items()}\n", " ylabel_key2i ={v:k for k,v in ylabel_i2key.items()}\n", "\n", " #construct keys for the grid\n", " self.key_gindex={(x+str(y)):(xlabel_key2i[x],ylabel_key2i[y]) for x in ['a','b','c','d','e','f'] for y in [1,2,3,4,5,6]}\n", " self.gindex_key ={v:k for k,v in self.key_gindex.items()}\n", "\n", " #kight moves on grid\n", " self.moves = [(2,1),(1,2),(-1,2),(-2,1),(-2,-1),(-1,-2),(1,-2),(2,-1)]\n", "\n", " self.dims = dims\n", " self.successfull_tours = None\n", " self.ls_knighttours = None\n", " self.score=np.zeros(dtype='int',shape=dims)\n", "\n", " def scoregrid (self, A,B,C):\n", "\n", " self.score[:,:]=B \n", " self.score[:,0]=A\n", " self.score[-4:,1]=A\n", " self.score[-2:,2]=A\n", " self.score[:,-1]=C\n", " self.score[:4,-2]=C\n", " self.score[:2,-3]=C\n", " #return self.score\n", " \n", " def calcscore_at_nextpos(self,prev_score,init_pos,next_pos):\n", " #care numpy array indexing, row first then column\n", " #pos is x,y == (column,row) therefore reverse indexing\n", "\n", " if self.score[init_pos[::-1]]==self.score[next_pos[::-1]]:\n", " return prev_score+self.score[next_pos[::-1]]\n", " else:\n", " return prev_score*self.score[next_pos[::-1]]\n", "\n", "\n", " score = prev_score[next_pos] - prev_score[init_pos]\n", " return score\n", "\n", "\n", " def tour(self,a,b,c,init_pos:tuple,dest_pos:tuple,numsteps:int):\n", " \n", " self.successfull_tours = None\n", " self.ls_knighttours = None\n", " self.scoregrid(a,b,c)\n", " score=a\n", " posstr = self.gindex_key[init_pos]\n", " ls_knighttours=KnightTour(init_pos,init_pos,posstr,score,posstr,1)\n", " \n", " self.run_knight_tours(numsteps,dest_pos,1,[ls_knighttours],[])\n", " #print(self.successfull_tours)\n", " return (pd.DataFrame([(t.tours,t.nummoves,t.currscore) for t in self.successfull_tours],\n", " columns=['tour','nmoves','score'])\n", " .assign(score=lambda df: df.apply(lambda r: calc_score_str(r['tour'],self.score),axis=1))\n", " .sort_values(by='score',ascending=False)\n", " .pipe(lambda x: x[x.score==2024])\n", " .head(1)\n", " )\n", " \n", "\n", "\n", "\n", " def run_knight_tours(self,numsteps,dest:tuple,iter,ls_knighttours:list,successfull_tours:list):\n", " #knight move in x,y coordinate system different to indexing of arrays!\n", " \n", " if iter == numsteps:\n", " self.ls_knighttours=ls_knighttours\n", " self.successfull_tours=successfull_tours\n", " return \n", " \n", " newscore=0\n", " newls_knighttours = [] \n", " for aknighttour in ls_knighttours:\n", "\n", " prevpos= aknighttour.prevpos\n", " pos= aknighttour.currpos\n", " score= aknighttour.currscore\n", "\n", " #check if the tour is complete!\n", " #if pos[0]==dest[0] and pos[1]==dest[1]: #reached f6 f1\n", " # successfull_tours.append(aknighttour)\n", " # continue\n", " \n", " for move in self.moves: #all possible moves\n", " x_new = pos[0]+move[0]\n", " y_new = pos[1]+move[1]\n", "\n", " \n", " if (x_new >= 0 and x_new< self.dims[0] and y_new >= 0 and y_new < self.dims[1]): #check if the move is valid \n", " \n", " #make sure new move does not go backwards!\n", " if (x_new == prevpos[0] and y_new == prevpos[1]):\n", " continue\n", " \n", "\n", " #newscore=self.calcscore_at_nextpos(score,(pos[0],pos[1]),(x_new,y_new))\n", " posstr = self.gindex_key[(x_new,y_new)]\n", "\n", " #now for each valid move, create corresponding paths/score\n", " updatedtour=aknighttour.tours +','+posstr\n", "\n", " newknighttour=KnightTour((x_new,y_new),pos,posstr,newscore,updatedtour,aknighttour.nummoves+1)\n", " if x_new==dest[0] and y_new==dest[1]: #reached f6 f1\n", " successfull_tours.append(newknighttour)\n", " else: \n", " newls_knighttours.append(newknighttour)\n", "\n", "\n", " return self.run_knight_tours(numsteps,dest,iter+1,newls_knighttours,successfull_tours)\n" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'a1,b3,d2,c4,d6,e4,f6'" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "N=7\n", "gobj=Grid()\n", "res_a1_f6=gobj.tour(1,2,253,(0,5),(5,0),N)\n", "\n", "res_a6_f1=gobj.tour(1,2,253,(0,0),(5,5),N)\n", "\n", "res_a1_f6.iloc[0,0]\n", "\n" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1 2 3 6\n" ] } ], "source": [ "import pandas as pd\n", "\n", "gobj=Grid()\n", "N=15\n", "\n", "counter=0\n", "\n", "outputs=[]\n", "scores=[]\n", "ids=[]\n", "for i in range(1,51):\n", " for j in range(1,51):\n", " for k in range(1,51):\n", " if (i !=j) and (j!=k) and (i!=k):\n", " sum=i+j+k\n", " if sum<=6:\n", " #print(counter)\n", " print(i,j,k,sum)\n", " res_a6_f1=gobj.tour(i,j,k,(0,0),(5,5),N)\n", " res_a1_f6=gobj.tour(i,j,k,(0,5),(5,0),N)\n", "\n", "\n", " if res_a6_f1.empty==False and res_a1_f6.empty==False:\n", " if res_a1_f6.iloc[0,2]==res_a1_f6.iloc[0,2]:\n", " op=f'{i},{j},{k},{res_a1_f6.iloc[0,0]},{res_a6_f1.iloc[0,0]}'\n", " ids.append(counter)\n", " scores.append(sum)\n", " outputs.append(op)\n", " print(counter,sum,op) \n", " counter+=1\n", "\n", "\n" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[]" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "gobj.tour(40,5,6,(0,0),(5,5),4)\n", "gobj.successfull_tours" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "#code\n", "score=np.zeros(dtype='int',shape=(6,6))\n", "\n", "def encode_scoregrid(path:str):\n", " return { (j,i):x+str(y) for i,x in enumerate(['a','b','c','d','e','f']) for j,y in enumerate([1,2,3,4,5,6][::-1])}\n", "\n", "def decode_scoregrid():\n", " return { x+str(y):(j,i) for i,x in enumerate(['a','b','c','d','e','f']) for j,y in enumerate([1,2,3,4,5,6][::-1])}\n", " \n", "def f_scoregrid (A,B,C):\n", "\n", " score[:,:]=B \n", " score[:,0]=A\n", " score[-4:,1]=A\n", " score[-2:,2]=A\n", " score[:,-1]=C\n", " score[:4,-2]=C\n", " score[:2,-3]=C\n", " return score\n", "\n", "scoregrid=f_scoregrid(1,2,253)\n", "\n", "def calc_score_tuple(tuplepaths,scoregrid):\n", " \n", " prevpos=None\n", " score=None\n", " for i,pos in enumerate(tuplepaths):\n", " \n", " if i==0:\n", " prevpos=pos\n", " score=scoregrid[pos]\n", " elif scoregrid[prevpos]==scoregrid[pos]:\n", " score+=scoregrid[pos]\n", " prevpos=pos\n", " else:\n", " score*=scoregrid[pos]\n", " prevpos=pos\n", "\n", " return score\n", " \n", "def calc_score_str(path,scoregrid:str):\n", " decode=decode_scoregrid()\n", " path = path.split(',')\n", "\n", " return calc_score_tuple([decode[x] for x in path],scoregrid)\n", " \n", " \n", " \n", " # return res " ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "ename": "NameError", "evalue": "name 'calc_score' is not defined", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", "Cell \u001b[0;32mIn[3], line 4\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;66;03m#a6,b4,c6,d4,f3,d2,e4,f6, score=111\u001b[39;00m\n\u001b[1;32m 2\u001b[0m scoregrid\u001b[38;5;241m=\u001b[39mf_scoregrid(\u001b[38;5;241m1\u001b[39m,\u001b[38;5;241m2\u001b[39m,\u001b[38;5;241m253\u001b[39m)\n\u001b[0;32m----> 4\u001b[0m \u001b[43mcalc_score\u001b[49m(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124ma6,b4,c6,d4,f3,d2,e4,f6\u001b[39m\u001b[38;5;124m'\u001b[39m,scoregrid)\n", "\u001b[0;31mNameError\u001b[0m: name 'calc_score' is not defined" ] } ], "source": [ "#a6,b4,c6,d4,f3,d2,e4,f6, score=111\n", "scoregrid=f_scoregrid(1,2,253)\n", "\n", "calc_score('a6,b4,c6,d4,f3,d2,e4,f6',scoregrid)" ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[np.int64(2024),\n", " np.int64(2024),\n", " np.int64(2024),\n", " np.int64(2024),\n", " np.int64(2024),\n", " np.int64(2024),\n", " np.int64(2024)]" ] }, "execution_count": 38, "metadata": {}, "output_type": "execute_result" } ], "source": [ "scores=['a6,b4,a2,c3,d5,e3,f1',\n", "'a6,c5,b3,a5,c4,d2,f1',\n", "'a6,b4,c2,d4,f5,e3,f1',\n", "'a6,b4,c6,d4,f5,e3,f1',\n", "'a6,c5,a4,c3,b1,d2,f1',\n", "'a6,c5,a4,b2,c4,d2,f1',\n", "'a6,c5,a4,b6,c4,d2,f1'\n", "]\n", "\n", "[calc_score_str(x,scoregrid) for x in scores]\n", "\n", "#calc_score_str('a1,b3,c5,d3,f4,d5,f6',scoregrid)\n", "#calc_score_str('a6,c5,a4,b2,c4,d2,f1',scoregrid)\n", "#calc_score_tuple([(5, 0), (3, 1), (1, 2), (3, 3), (2, 5), (1, 3), (0, 5)],scoregrid)\n" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "np.int64(2024)" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "np.int64(2024)" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "scoregrid=f_scoregrid(1,5,2)\n", "calc_score_str('a1,c2,d4,f3,d2,f1,e3,d1,f2,e4,f6',scoregrid)\n", "calc_score_str('a6,c5,d3,e5,c4,d6,e4,c3,d5,e3,f1',scoregrid)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# joojoo score" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "np.int64(2024)" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "np.int64(2024)" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import numpy as np\n", "scoregrid=f_scoregrid(3,1,2)\n", "calc_score_str('a1,c2,e3,c4,a5,c6,d4,b3,c5,d3,b2,d1,c3,b1,d2,e4,f6',scoregrid)\n", "\n", "calc_score_str('a6,b4,d5,c3,a4,c5,d3,c1,e2,d4,b3,d2,c4,b2,d1,e3,f1',scoregrid)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# joojoo score v2" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "np.int64(2024)" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "np.int64(2024)" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import numpy as np\n", "scoregrid=f_scoregrid(1,3,2)\n", "calc_score_str('a1,c2,b4,c6,e5,d3,f2,e4,d6,c4,a3,b1,c3,d5,f6',scoregrid)\n", "\n", "calc_score_str('a6,b4,c2,e1,f3,d4,f5,d6,e4,c3,b1,a3,c4,e3,f1',scoregrid)" ] }, { "cell_type": "code", "execution_count": 74, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[(5, 0), (3, 1), (1, 2), (3, 3), (2, 5), (1, 3), (0, 5)]" ] }, "execution_count": 74, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "[(0, 0), (1, 2), (2, 0), (4, 1), (2, 2), (4, 3), (5, 5)]" ] }, "execution_count": 74, "metadata": {}, "output_type": "execute_result" } ], "source": [ "decode=decode_scoregrid()\n", "[decode[k] for k in 'a1,b3,c5,d3,f4,d5,f6'.split(',')]\n", "[decode[k] for k in 'a6,c5,a4,b2,c4,d2,f1'.split(',')]" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "General", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.2" } }, "nbformat": 4, "nbformat_minor": 2 }