{
 "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
}
