![]() StartingPoints Referenced by
|
The Code So Far - A Better Life And Death Solver
Keywords: Life & Death
My new solver can deal with n-step problems, and even print out a Sensei's Library compatible solution. Not bad for 650 lines of code! I'll add text/code in a little while: right now I'm off to drink a celebratory beer or six. * (printsl (solve (nth 23 *ps1*)))
The class responsible for look-ahead is the analyzer: (defclass Analyzer () ((p :documentation "the problem being solved") (g :documentation "the current game state") (goal :documentation "the goal of this analyzer") (answer :documentation "success or failure") (resolved :documentation "t if answer is known") (children :documentation "the tree of games after this game state") (active-children :documentation "unresolved children") (next :documentation "next active child to get a tick")) The analyzer has a problem and a current game state. It either declares the problem solved, or creates child analyzers that do the min-max search (each child has the inverse goal of its parent: SaveG, save a goup, is the inverse goal of KillG, kill a group, etc.) "Tick" is the unit of computation: one tick is the time to create a new game state (e.g. playing a stone.) When an Analyzer gets a tick, it can either create a new game state, or pass the tick onto one of its children (round-robin style.) For now, my code doesn't obey the one game-state per tick design goal. Varously crappy short-cuts are also used in deciding life and death (e.g. 3 or more liberties means a group is safe,) but these are probably adequate for GGPFB1. Observers will have noted that main classes (Position, Game, Analyzer) are all non-destructive (functional) in that they don't alter themselves, they create new versions of themselves that reflect the state change (e.g. a Position plus a Stone creates a new Position without changing the old Position.) This is by design: I get confused when things change underneath me. The above diagrams were honestly generated by the code! This method prints the initial problem and the solution: (defmethod printsl ((a Analyzer) &optional text) (if text (print text)) (printsl (p a)) (format t *line-break*) (if (resolved a) (printsl (main-line a) (if (eq (current-player (game (p a))) *White*) "W Solution" "B Solution")))) This one does the 1,2,3 thing to explain the play: (defmethod printsl ((l Line) &optional text) (let* ((as (as l)) (pos (copy (pos (g (car as))))) (i 1)) (dolist (e as) (setf (at pos (last-move (g e))) (make-instance 'Stone 'PChar (character (format nil "~S" i)))) (setf i (1+ i))) (printsl pos text))) And this one marks the stones that are the focus of the problem: (defmethod printsl ((p Problem) &optional text) (if text (print text)) (let ((text (cond ((eq (Action (Goal p)) 'KillG ) (format nil " ~A to kill the marked stones." (name (current-player (game p))))) ((eq (Action (Goal p)) 'SaveG ) (format nil " ~A to save the marked stones." (name (current-player (game p))))) (t "Unknown goal.")))) (let ((pos (copy (Pos (Game p))))) (cond ((or (eq (Action (Goal p)) 'KillG ) (eq (Action (Goal p)) 'SaveG )) (mapcar (lambda (pnt) (setf (at pos pnt) (make-instance 'Stone 'PChar (MChar (at pos pnt))))) (points (at (smap (snake-set (game p))) (Focus (Goal p))))))) (printsl pos text)))) Hehe, I love my code: * (printsl (solve (nth 24 *ps1*)))
The new code tears through Graded Go Problems For Beginners life and death problems until it hits Problem #41. This is a copy of the living page "The Code So Far - A Better Life And Death Solver" at Sensei's Library. ![]() |