From e5cf760e8a128aa56a2be0752b8cbf30e804408b Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Fri, 3 Sep 2010 10:52:45 -0700 Subject: [PATCH] solitaire: an exercise in backtracking and string conversions 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 | 119 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 test/solitaire.go diff --git a/test/solitaire.go b/test/solitaire.go new file mode 100644 index 0000000000..c789bf24a0 --- /dev/null +++ b/test/solitaire.go @@ -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") +}