Files
notes/code/misc/python/scripts/KnighTour.ipynb
2024-12-20 21:50:09 +00:00

612 lines
18 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"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 knights 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
}