]> Cypherpunks repositories - gostls13.git/commitdiff
solitaire: an exercise in backtracking and string conversions
authorRobert Griesemer <gri@golang.org>
Fri, 3 Sep 2010 17:52:45 +0000 (10:52 -0700)
committerRobert Griesemer <gri@golang.org>
Fri, 3 Sep 2010 17:52:45 +0000 (10:52 -0700)
Solves the (English) peg solitaire game. The board is represented
by a 1-dimensional array for easy representation of directions
with a single integer. The board's contents are chosen such that
it can be printed with a direct string() conversion.

R=r
CC=adg, golang-dev
https://golang.org/cl/2066042

test/solitaire.go [new file with mode: 0644]

diff --git a/test/solitaire.go b/test/solitaire.go
new file mode 100644 (file)
index 0000000..c789bf2
--- /dev/null
@@ -0,0 +1,119 @@
+// $G $F.go && $L $F.$A  # don't run it - produces too much output
+
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This program solves the (English) peg solitaire board game.
+// See also: http://en.wikipedia.org/wiki/Peg_solitaire
+
+package main
+
+const N = 11 + 1 // length of a board row (+1 for newline)
+
+// The board must be surrounded by 2 illegal fields in each direction
+// so that move() doesn't need to check the board boundaries. Periods
+// represent illegal fields, ● are pegs, and ○ are holes.
+var board = []int(
+       `...........
+...........
+....●●●....
+....●●●....
+..●●●●●●●..
+..●●●○●●●..
+..●●●●●●●..
+....●●●....
+....●●●....
+...........
+...........
+`)
+
+
+// center is the position of the center hole if there is a single one;
+// otherwise it is -1.
+var center int
+
+func init() {
+       n := 0
+       for pos, field := range board {
+               if field == '○' {
+                       center = pos
+                       n++
+               }
+       }
+       if n != 1 {
+               center = -1 // no single hole
+       }
+}
+
+
+var moves int // number of times move is called
+
+// move tests if there is a peg at position pos that can jump over another peg
+// in direction dir. If the move is valid, it is executed and move returns true.
+// Otherwise, move returns false.
+func move(pos, dir int) bool {
+       moves++
+       if board[pos] == '●' && board[pos+dir] == '●' && board[pos+2*dir] == '○' {
+               board[pos] = '○'
+               board[pos+dir] = '○'
+               board[pos+2*dir] = '●'
+               return true
+       }
+       return false
+}
+
+
+// unmove reverts a previously executed valid move.
+func unmove(pos, dir int) {
+       board[pos] = '●'
+       board[pos+dir] = '●'
+       board[pos+2*dir] = '○'
+}
+
+
+// solve tries to find a sequence of moves such that there is only one peg left
+// at the end; if center is >= 0, that last peg must be in the center position.
+// If a solution is found, solve prints the board after each move in a backward
+// fashion (i.e., the last board position is printed first, all the way back to
+// the starting board position).
+func solve() bool {
+       var last, n int
+       for pos, field := range board {
+               // try each board position
+               if field == '●' {
+                       // found a peg
+                       for _, dir := range [...]int{-1, -N, +1, +N} {
+                               // try each direction
+                               if move(pos, dir) {
+                                       // a valid move was found and executed,
+                                       // see if this new board has a solution
+                                       if solve() {
+                                               unmove(pos, dir)
+                                               println(string(board))
+                                               return true
+                                       }
+                                       unmove(pos, dir)
+                               }
+                       }
+                       last = pos
+                       n++
+               }
+       }
+       // tried each possible move
+       if n == 1 && (center < 0 || last == center) {
+               // there's only one peg left
+               println(string(board))
+               return true
+       }
+       // no solution found for this board
+       return false
+}
+
+
+func main() {
+       if !solve() {
+               println("no solution found")
+       }
+       println(moves, "moves tried")
+}