I Oc*@s3dZddlTddlZddlZddlZddlZddlZddlZdefdYZ dddYZ ddd YZ d Z d Z d Zd ZdZdZdZdZddZdZeZddZddZdZddddZedZdZdZdZd d!dd"Z d d!d#Z!d$dd%YZ"d&dd'YZ#dd(Z$e%d)d*d+d,d-d.Z&e$e'd/e'd0d1d2d3d4d5d6e'd7d8d9d:d;d<d=d>d?e'd@dAdBdCd9dDd@e'dEd1dFe'dGdHd=e'd2dIdGe'd7dJdKe'dLdMdNdOdPe'd4dQdEdRdSe'd0dTd2dUd9e'dBdVdBe'd2dWd7e'dLdX Z(e'd/dd6dd?dd@ddFdd=dd;ddGddKddPddEddNddSdd9ddBdd2dd4dd7ddLdd0de(_)e$e'd4e'd}e'd~ddddddddLdde'd~dddde'dddLdZ*e'd~dddd}dddddd4ddLde*_)de fdYZ+de fdYZ,dZ-ddddddddddddddddgZ.ddZ/e0dZ1dZ2idZ3dZ4dddYZ5dddYZ6dde7dZ8dZ9de fdYZ:eeeeeegdZ;dZ<ed7Zee=d7ZdS(sSearch (Chapters 3-4) The way to use this code is to subclass Problem to create a class of problems, then create problem instances and solve them with calls to the various search functions.i(t*NtProblemcBsGeZdZddZdZdZdZdZdZ RS(sThe abstract class for a formal problem. You should subclass this and implement the methods actions and result, and possibly __init__, goal_test, and path_cost. Then you will create instances of your subclass and solve them with the various search functions.cCs||_||_dS(sThe constructor specifies the initial state, and possibly a goal state, if there is a unique goal. Your subclass's constructor can add other arguments.N(tinitialtgoal(tselfRR((s-C:\Users\\Desktop\Problem1_5\search.pyt__init__s cCstdS(sReturn the actions that can be executed in the given state. The result would typically be a list, but if there are many actions, consider yielding them one at a time in an iterator, rather than building them all at once.N(tabstract(Rtstate((s-C:\Users\\Desktop\Problem1_5\search.pytactionsscCstdS(sReturn the state that results from executing the given action in the given state. The action must be one of self.actions(state).N(R(RRtaction((s-C:\Users\\Desktop\Problem1_5\search.pytresultscCs ||jkS(sReturn True if the state is a goal. The default method compares the state to self.goal, as specified in the constructor. Override this method if checking against a single self.goal is not enough.(R(RR((s-C:\Users\\Desktop\Problem1_5\search.pyt goal_test%scCs|dS(stReturn the cost of a solution path that arrives at state2 from state1 via action, assuming cost c to get up to state1. If the problem is such that the path doesn't matter, this function will only look at state2. If the path does matter, it will consider c and maybe state1 and action. The default method costs 1 for every step in the path.i((Rtctstate1R tstate2((s-C:\Users\\Desktop\Problem1_5\search.pyt path_cost+scCstdS(s|For optimization problems, each state has a value. Hill-climbing and related algorithms try to maximize this value.N(R(RR((s-C:\Users\\Desktop\Problem1_5\search.pytvalue3sN( t__name__t __module__t__doc__tNoneRRR R RR(((s-C:\Users\\Desktop\Problem1_5\search.pyR s     tNodecBs_eZdZd d ddZdZdZdZdZdZ dZ d Z RS( s0A node in a search tree. Contains a pointer to the parent (the node that this is a successor of) and to the actual state for this node. Note that if a state is arrived at by two paths, then there are two nodes with the same state. Also includes the action that got us to this state, and the total path_cost (also known as g) to reach the node. Other functions may add an f and h value; see best_first_graph_search and astar_search for an explanation of how the f and h values are handled. You will not need to subclass this class.ic CsEt|d|d|d|d|dd|rA|jd|_ndS( s>Create a search tree Node, derived from a parent by an action.RtparentR RtdepthiiN(tupdateR(RRRR R((s-C:\Users\\Desktop\Problem1_5\search.pyRCs cCsd|jfS(Ns (R(R((s-C:\Users\\Desktop\Problem1_5\search.pyt__repr__JscCs/g|j|jD]}|j||^qS(s4List the nodes reachable in one step from this node.(RRt child_node(RtproblemR ((s-C:\Users\\Desktop\Problem1_5\search.pytexpandMsc Cs@|j|j|}t||||j|j|j||S(s Fig. 3.10(R RRR(RRR tnext((s-C:\Users\\Desktop\Problem1_5\search.pyRRs cCs$g|jdD]}|j^qS(s@Return the sequence of actions to go from the root to this node.i(tpathR (Rtnode((s-C:\Users\\Desktop\Problem1_5\search.pytsolutionXscCs@|g}}x |r/|j||j}qWtt|S(sCReturn a list of nodes forming the path from the root to this node.(tappendRtlisttreversed(RRt path_back((s-C:\Users\\Desktop\Problem1_5\search.pyR\s     cCst|to|j|jkS(N(t isinstanceRR(Rtother((s-C:\Users\\Desktop\Problem1_5\search.pyt__eq__iscCs t|jS(N(thashR(R((s-C:\Users\\Desktop\Problem1_5\search.pyt__hash__lsN( RRRRRRRRR RR'R)(((s-C:\Users\\Desktop\Problem1_5\search.pyR9s     t SimpleProblemSolvingAgentProgramcBsGeZdZddZdZdZdZdZdZ RS(s:Abstract framework for a problem-solving agent. [Fig. 3.1]cCst|d|dgdS(NRtseq(R(Rt initial_state((s-C:\Users\\Desktop\Problem1_5\search.pyRsscCsz|j|j||_|jsj|j|j}|j|j|}|j||_|jsjdSn|jjdS(Ni(t update_stateRR+tformulate_goaltformulate_problemtsearchRtpop(RtperceptRR((s-C:\Users\\Desktop\Problem1_5\search.pyt__call__vs  cCstdS(N(R(RR2((s-C:\Users\\Desktop\Problem1_5\search.pyR-scCstdS(N(R(RR((s-C:\Users\\Desktop\Problem1_5\search.pyR.scCstdS(N(R(RRR((s-C:\Users\\Desktop\Problem1_5\search.pyR/scCstdS(N(R(RR((s-C:\Users\\Desktop\Problem1_5\search.pyR0sN( RRRRRR3R-R.R/R0(((s-C:\Users\\Desktop\Problem1_5\search.pyR*qs    cCs_|jt|jxB|rZ|j}|j|jrA|S|j|j|qWdS(sSearch through the successors of a problem to find a goal. The argument frontier should be an empty queue. Don't worry about repeated paths to a state. [Fig. 3.7]N( R!RRR1R RtextendRR(RtfrontierR((s-C:\Users\\Desktop\Problem1_5\search.pyt tree_searchs  csjt|jtxerj}|j|jrJ|Sj|jjfd|j |Dq"WdS(sSearch through the successors of a problem to find a goal. The argument frontier should be an empty queue. If two paths reach a state, only use the first one. [Fig. 3.7]c3s0|]&}|jkr|kr|VqdS(N(R(t.0tchild(texploredR5(s-C:\Users\\Desktop\Problem1_5\search.pys sN( R!RRtsetR1R RtaddR4RR(RR5R((R5R9s-C:\Users\\Desktop\Problem1_5\search.pyt graph_searchs   -cCst|tS(s5Search the shallowest nodes in the search tree first.(R6t FIFOQueue(R((s-C:\Users\\Desktop\Problem1_5\search.pytbreadth_first_tree_searchscCst|tS(s2Search the deepest nodes in the search tree first.(R6tStack(R((s-C:\Users\\Desktop\Problem1_5\search.pytdepth_first_tree_searchscCst|tS(s2Search the deepest nodes in the search tree first.(R<R?(R((s-C:\Users\\Desktop\Problem1_5\search.pytdepth_first_graph_searchscCst|j}|j|jr%|St}|j|t}x|r|j}|j|jxX|j |D]G}|j|kry||kry|j|jr|S|j|qyqyWqGWdS(s [Fig. 3.11]N( RRR RR=R!R:R1R;RR(RRR5R9R8((s-C:\Users\\Desktop\Problem1_5\search.pytbreadth_first_searchs     cCsCt|d}t|j}|j|jr4|Stt|}|j|t}x|r>|j }|j|jrdGt j j t ||S|j|jx|j|D]y}|j|kr||kr|j|q||kr||}||||kr7||=|j|q7qqWq\WdS(sSearch the nodes with the lowest f scores first. You specify the function f(node) that you want to minimize; for example, if f is a heuristic estimate to the goal, then we have greedy best first search; if f is node.depth then we have breadth-first search. There is a subtlety: the line "f = memoize(f, 'f')" means that the f values will be cached on the nodes as they are computed. So after doing a best first search you can examine the f values of the path returned.tfsTotal nodes explored : N(tmemoizeRRR Rt PriorityQueuetminR!R:R1tsyststdouttwritetlenR;RR(RRCRR5R9R8t incumbent((s-C:\Users\\Desktop\Problem1_5\search.pytbest_first_graph_searchs.      cCst|dS(s [Fig. 3.14]cSs|jS(N(R(R((s-C:\Users\\Desktop\Problem1_5\search.pyts(RL(R((s-C:\Users\\Desktop\Problem1_5\search.pytuniform_cost_searchsi2cs(fdt|j||S(s [Fig. 3.17]cs|j|jr|S|j|kr)dSt}xN|j|D]=}|||}|dkrlt}q?|dk r?|Sq?Wt|ddSdS(Ntcutoff(R RRtFalseRtTrueRtif_(RRtlimittcutoff_occurredR8R (t recursive_dls(s-C:\Users\\Desktop\Problem1_5\search.pyRUs   (RR(RRS((RUs-C:\Users\\Desktop\Problem1_5\search.pytdepth_limited_searchscCs=x6ttjD]%}t||}|dkr|SqWdS(s [Fig. 3.18]RON(txrangeRGtmaxintRV(RRR ((s-C:\Users\\Desktop\Problem1_5\search.pytiterative_deepening_searchs cs.tp|jdt|fdS(sA* search is best-first graph search with f(n) = g(n)+h(n). You need to specify the h function when you call astar_search, or else in your Problem subclass.thcs|j|S(N(R(tn(RZ(s-C:\Users\\Desktop\Problem1_5\search.pyRMs(RDRZRL(RRZ((RZs-C:\Users\\Desktop\Problem1_5\search.pyt astar_search scsdtp|jdfdt|j}||_||t\}}|S(s [Fig. 3.26]RZcs&|j|jr|dfS|j|}t|dkrGdtfSx0|D](}t|j||j|_qNWxt r!|j d|d}|j|krd|jfSt|dkr|dj}nt}||t ||\}|_|dk r}||jfSq}WdS(NicSst|j|jS(N(tcmpRC(txty((s-C:\Users\\Desktop\Problem1_5\search.pyRM$si( R RRRJRtinfinitytmaxRRCRQtsortRF(RRtflimitt successorststbestt alternativeR (RZtRBFS(s-C:\Users\\Desktop\Problem1_5\search.pyRhs$   &   $ (RDRZRRRCR`(RRZRR tbestf((RZRhs-C:\Users\\Desktop\Problem1_5\search.pytrecursive_best_first_searchs cstj}xitrz|j}|s1Pnt|fd}j|jj|jkrqPn|}qW|jS(syFrom the initial node, keep choosing the neighbor with highest value, stopping when no neighbor is better. [Fig. 4.2]csj|jS(N(RR(R(R(s-C:\Users\\Desktop\Problem1_5\search.pyRM>s(RRRQRtargmax_random_tieRR(Rtcurrentt neighborstneighbor((Rs-C:\Users\\Desktop\Problem1_5\search.pyt hill_climbing5s $ ig{Gzt?idcsfdS(s6One possible schedule function for simulated annealingcs(t|ktj |dS(Ni(RRtmathtexp(tt(tlamtkRS(s-C:\Users\\Desktop\Problem1_5\search.pyRMFs((RtRsRS((RsRtRSs-C:\Users\\Desktop\Problem1_5\search.pyt exp_scheduleDscCst|j}xttjD]}||}|dkrA|S|j|}|sZ|Stj|}|j|j |j|j }|dkst t j ||r|}qqWdS(s [Fig. 4.5]iN( RRRWRGRXRtrandomtchoiceRRt probabilityRpRq(RtscheduleRlRrtTRmRtdelta_e((s-C:\Users\\Desktop\Problem1_5\search.pytsimulated_annealingHs  "%cCs tdS(s [Fig. 4.11]N(t unimplemented(R((s-C:\Users\\Desktop\Problem1_5\search.pytand_or_graph_searchWscCs tdS(s [Fig. 4.21]N(R}(ts1((s-C:\Users\\Desktop\Problem1_5\search.pytonline_dfs_agent[scCs tdS(s [Fig. 4.24]N(R}(R((s-C:\Users\\Desktop\Problem1_5\search.pytlrta_star_agent_sig?cCs^|j}g|j|D]}|j||^q}tj|t|| |j||S(sCall genetic_algorithm on the appropriate parts of a problem. This requires the problem to have states that can mate and mutate, plus a value method that scores states.(R,RR Rvtshuffletgenetic_algorithmR(Rt fitness_fntngentpmutR[Retatstates((s-C:\Users\\Desktop\Problem1_5\search.pytgenetic_searchfs . c Csxt|D]}g}x|t|D]n}t||}t||d\}}|j|} tjdd|kr| jn|j| q&W|}q Wt ||S(s [Fig. 4.8]iii( trangeRJtmapt weighted_sample_with_replacementtmateRvtuniformtmutateR!targmax( t populationRRRtitnew_populationt fitnessestp1tp2R8((s-C:\Users\\Desktop\Problem1_5\search.pyRos  tGAStatecBs)eZdZdZdZdZRS(s3Abstract class for individuals in a genetic search.cCs ||_dS(N(tgenes(RR((s-C:\Users\\Desktop\Problem1_5\search.pyRscCs7tjt|j}|j|j| |j|S(s0Return a new individual crossing self and other.(Rvt randrangeRJRt __class__(RR&R ((s-C:\Users\\Desktop\Problem1_5\search.pyRscCstdS(sChange a few of my genes.N(R(R((s-C:\Users\\Desktop\Problem1_5\search.pyRs(RRRRRR(((s-C:\Users\\Desktop\Problem1_5\search.pyR}s  tGraphcBsPeZdZdedZdZddZdZddZ dZ RS( sFA graph connects nodes (verticies) by edges (links). Each edge can also have a length associated with it. The constructor call is something like: g = Graph({'A': {'B': 1, 'C': 2}) this makes a graph with 3 nodes, A, B, and C, with an edge of length 1 from A to B, and an edge of length 2 from A to C. You can also do: g = Graph({'A': {'B': 1, 'C': 2}, directed=False) This makes an undirected graph, so inverse links are also added. The graph stays undirected; if you add more links with g.connect('B', 'C', 3), then inverse link is also added. You can use g.nodes() to get a list of nodes, g.get('A') to get a dict of links out of A, and g.get('A', 'B') to get the length of the link from A to B. 'Lengths' can actually be any object at all, and nodes can be any hashable object.cCs/|p i|_||_|s+|jndS(N(tdicttdirectedtmake_undirected(RRR((s-C:\Users\\Desktop\Problem1_5\search.pyRs cCsUxN|jjD]=}x4|j|jD]\}}|j|||q*WqWdS(sBMake a digraph into an undirected graph by adding symmetric edges.N(Rtkeystitemstconnect1(RRtbtdistance((s-C:\Users\\Desktop\Problem1_5\search.pyRs icCs6|j||||js2|j|||ndS(slAdd a link from A and B of given distance, and also add the inverse link if the graph is undirected.N(RR(RtAtBR((s-C:\Users\\Desktop\Problem1_5\search.pytconnects cCs||jj|i|>> depth_first_tree_search(NQueensProblem(8)) cCs||_dg||_dS(N(RRR(RR((s-C:\Users\\Desktop\Problem1_5\search.pyRs cCs\|ddk rgS|jd}gt|jD]!}|j|||s3|^q3SdS(s;In the leftmost empty column, try all non-conflicting rows.iN(RtindexRRt conflicted(RRtcoltrow((s-C:\Users\\Desktop\Problem1_5\search.pyR#s cCs$|jd}|}|||<|S(s&Place the next queen at the given row.N(RR(RRRRtnew((s-C:\Users\\Desktop\Problem1_5\search.pyR ,s cs)tfdtDS(s;Would placing a queen at (row, col) conflict with anything?c3s+|]!}j||VqdS(N(tconflict(R7R (RRRR(s-C:\Users\\Desktop\Problem1_5\search.pys 5s(tanyR(RRRR((RRRRs-C:\Users\\Desktop\Problem1_5\search.pyR3scCs>||kp=||kp=||||kp=||||kS(sCWould putting two queens in (row1, col1) and (row2, col2) conflict?((Rtrow1tcol1trow2tcol2((s-C:\Users\\Desktop\Problem1_5\search.pyR8s  cs>ddkrtStfdttD S(s*Check if all columns filled, no conflicts.ic3s(|]}j||VqdS(N(R(R7R(RR(s-C:\Users\\Desktop\Problem1_5\search.pys CsN(RRPRRRJ(RR((RRs-C:\Users\\Desktop\Problem1_5\search.pyR ?s( RRRRRR RRR (((s-C:\Users\\Desktop\Problem1_5\search.pyRs    tABCDEFGHIJKLMNOPQRSTUVWXYZtFORIXBtMOQABJtGURILWtSETUPLtCMPDAEtACITAOtSLCRAEtROMASHtNODESWtHEFIYEtONUDTKtTEVIGNtANEDVZtPINESHtABILYTtGKYLEUicCsHgt||D]}t|d^q}tj|ttj|S(saReturn a random Boggle board of size n x n. We represent a board as a linear list of letters.i(Rtcubes16RvRRRw(R[Rtcubes((s-C:\Users\\Desktop\Problem1_5\search.pyt random_boggleQs+ tRSTCSDEIAEGNLRPEATESMSSIDcCs}t|}t|}x]t|D]O}||dkrK|dkrKHn||dkrbdGq%t||dGq%WHdS(sPrint the board in a 2-d array.iRtQut N(RJt exact_sqrtRtstr(tboardtn2R[R((s-C:\Users\\Desktop\Problem1_5\search.pyt print_boggle]s c Cs|j|r|j|St|}dg|}x^t|D]P}g||<||k}|||k}||dk}|d|dk}|s||j|||s||j||dn|s||j||dqn|sV||j|||s1||j||dn|sV||j||dqVn|st||j|dn|sB||j|dqBqBW|||<|S(shReturn a list of lists, where the i-th element is the list of indexes for the neighbors of square i.iiN(RRRRR!( RtcacheR[RmRton_topt on_bottomton_leftton_right((s-C:\Users\\Desktop\Problem1_5\search.pytboggle_neighborsfs8      cCs/ttj|}|||ks+t|S(sDIf n2 is a perfect square, return its square root, else raise error.(RRptsqrttAssertionError(RR[((s-C:\Users\\Desktop\Problem1_5\search.pyRstWordlistcBs;eZdZddZdddZdZdZRS(sThis class holds a list of words. You can use (word in wordlist) to check if a word is in the list, or wordlist.lookup(prefix) to see if prefix starts any of the words in the list.icCst|jjj}g|D]}t||kr%|^q%|_|jji|_xUtD]M}t t |d}t j |j|t j |j|f|j|i(R2R3RRR(R((s-C:\Users\\Desktop\Problem1_5\search.pyR"s( RRRRRR R RRR5R(((s-C:\Users\\Desktop\Problem1_5\search.pyR1s       cCs\d}g|D]5}t|gg|D]}|||^q)^q}t||dS(NcSst|}|||S(N(R1(tsearcherRtp((s-C:\Users\\Desktop\Problem1_5\search.pytdo,s  (tnamet print_table(tproblemstheadert searchersR8ReR7ttable((s-C:\Users\\Desktop\Problem1_5\search.pytcompare_searchers&s BcCsPtdtddttddttddtgdd d d d gd S(sPrints a table of results like this: >>> compare_graph_searchers() Searcher Romania(A, B) Romania(O, N) Australia breadth_first_tree_search < 21/ 22/ 59/B> <1158/1159/3288/N> < 7/ 8/ 22/WA> breadth_first_search < 7/ 11/ 18/B> < 19/ 20/ 45/N> < 2/ 6/ 8/WA> depth_first_graph_search < 8/ 9/ 20/B> < 16/ 17/ 38/N> < 4/ 5/ 11/WA> iterative_deepening_search < 11/ 33/ 31/B> < 656/1815/1812/N> < 3/ 11/ 11/WA> depth_limited_search < 54/ 65/ 185/B> < 387/1012/1125/N> < 50/ 54/ 200/WA> recursive_best_first_search < 5/ 6/ 15/B> <5887/5888/16532/N> < 11/ 12/ 43/WA>R;RRRRRRR<tSearchers Romania(A, B)s Romania(O, N)t AustraliaN(R?Rtromaniat australia(((s-C:\Users\\Desktop\Problem1_5\search.pytcompare_graph_searchers3s s >>> ab = GraphProblem('A', 'B', romania) >>> breadth_first_tree_search(ab).solution() ['S', 'F', 'B'] >>> breadth_first_search(ab).solution() ['S', 'F', 'B'] >>> uniform_cost_search(ab).solution() ['S', 'R', 'P', 'B'] >>> depth_first_graph_search(ab).solution() ['T', 'L', 'M', 'D', 'C', 'P', 'B'] >>> iterative_deepening_search(ab).solution() ['S', 'F', 'B'] >>> len(depth_limited_search(ab).solution()) 50 >>> astar_search(ab).solution() ['S', 'R', 'P', 'B'] >>> recursive_best_first_search(ab).solution() ['S', 'R', 'P', 'B'] >>> board = list('SARTELNID') >>> print_boggle(board) S A R T E L N I D >>> f = BoggleFinder(board) >>> len(f) 206 s >>> ' '.join(f.words()) 'LID LARES DEAL LIE DIETS LIN LINT TIL TIN RATED ERAS LATEN DEAR TIE LINE INTER STEAL LATED LAST TAR SAL DITES RALES SAE RETS TAE RAT RAS SAT IDLE TILDES LEAST IDEAS LITE SATED TINED LEST LIT RASE RENTS TINEA EDIT EDITS NITES ALES LATE LETS RELIT TINES LEI LAT ELINT LATI SENT TARED DINE STAR SEAR NEST LITAS TIED SEAT SERAL RATE DINT DEL DEN SEAL TIER TIES NET SALINE DILATE EAST TIDES LINTER NEAR LITS ELINTS DENI RASED SERA TILE NEAT DERAT IDLEST NIDE LIEN STARED LIER LIES SETA NITS TINE DITAS ALINE SATIN TAS ASTER LEAS TSAR LAR NITE RALE LAS REAL NITER ATE RES RATEL IDEA RET IDEAL REI RATS STALE DENT RED IDES ALIEN SET TEL SER TEN TEA TED SALE TALE STILE ARES SEA TILDE SEN SEL ALINES SEI LASE DINES ILEA LINES ELD TIDE RENT DIEL STELA TAEL STALED EARL LEA TILES TILER LED ETA TALI ALE LASED TELA LET IDLER REIN ALIT ITS NIDES DIN DIE DENTS STIED LINER LASTED RATINE ERA IDLES DIT RENTAL DINER SENTI TINEAL DEIL TEAR LITER LINTS TEAL DIES EAR EAT ARLES SATE STARE DITS DELI DENTAL REST DITE DENTIL DINTS DITA DIET LENT NETS NIL NIT SETAL LATS TARE ARE SATI' >>> boggle_hill_climbing(list('ABCDEFGHI'), verbose=False) (['E', 'P', 'R', 'D', 'O', 'A', 'G', 'S', 'T'], 123) (((((i[i(iiG(ii (ii+(i2i%(i1i(iwi(ii^(ii(ii{(iiS(ii(ii;(i@ip(ii(ii(i^i(ii^(ii(ili(ixi(ii(ii(ii(ii (ii*(ii%(((>RtutilsRpRvRGttimeR tstringtobjectRRR*R6R<R>R@RARBRLRNRVRYtgreedy_best_first_graph_searchRR\RjRoRuR|R~RRRRRRRRRtDictRBRRCRRR RRR"t boyan_bestRRRRRRQR0R*R1R?RDt random_tests(((s-C:\Users\\Desktop\Problem1_5\search.pyts H-8              /  !  '4      #: %