mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2025-12-22 07:10:42 -08:00
244 lines
7.4 KiB
Plaintext
244 lines
7.4 KiB
Plaintext
print " "*32 + "Checkers"
|
|
print " "*15 + "Creative Computing Morristown, New Jersey"
|
|
print; print; print
|
|
|
|
print "This is the game of Checkers. The computer is X,"
|
|
print "and you are O. The computer will move first."
|
|
print "Squares are referred to by a coordinate system."
|
|
print "(0,0) is the lower left corner"
|
|
print "(0,7) is the upper left corner"
|
|
print "(7,0) is the lower right corner"
|
|
print "(7,7) is the upper right corner"
|
|
print "The computer will type '+TO' when you have another"
|
|
print "jump. Type two negative numbers if you cannot jump."
|
|
print; print; print
|
|
|
|
input "(Press Return.)"; print // (give player a chance to read)
|
|
|
|
// The board. Pieces are represented by numeric values:
|
|
// - 0 = empty square
|
|
// - -1,-2 = computer (X) (-1 for regular piece, -2 for king)
|
|
// - 1,2 = human (O) (1 for regular piece, 2 for king)
|
|
// Board is indexed by [x][y], so we have to initialize it sideways:
|
|
board = [
|
|
[ 1, 0, 1, 0, 0, 0, -1, 0],
|
|
[ 0, 1, 0, 0, 0, -1, 0, -1],
|
|
[ 1, 0, 1, 0, 0, 0, -1, 0],
|
|
[ 0, 1, 0, 0, 0, -1, 0, -1],
|
|
[ 1, 0, 1, 0, 0, 0, -1, 0],
|
|
[ 0, 1, 0, 0, 0, -1, 0, -1],
|
|
[ 1, 0, 1, 0, 0, 0, -1, 0],
|
|
[ 0, 1, 0, 0, 0, -1, 0, -1]]
|
|
|
|
// Function to print the board
|
|
printBoard = function
|
|
for y in range(7, 0)
|
|
for x in range(0, 7)
|
|
// We print by indexing with the board entry (-2 to 2) into a list
|
|
// of possible representations. Remember that in MiniScript, a
|
|
// negative index counts from the end.
|
|
print [". ", "O ", "O*", "X*", "X "][board[x][y]], " "
|
|
end for
|
|
print; print
|
|
end for
|
|
end function
|
|
|
|
// Function to get x,y coordinates from the player.
|
|
// This is written to allow the two numbers to be
|
|
// separated by any combination of ',' and ' '.
|
|
// Returns input as [x,y].
|
|
inputPosition = function(prompt, requiredPieceSign, allowCancel=false)
|
|
while true
|
|
ans = input(prompt + "? ").replace(",", " ").split
|
|
if ans.len < 2 then
|
|
print "Enter two coordinates, for example: 4 0"
|
|
continue
|
|
end if
|
|
x = val(ans[0])
|
|
y = val(ans[-1])
|
|
if x < 0 and y < 0 and allowCancel then
|
|
return [x, y]
|
|
else if x < 0 or x > 7 or y < 0 or y > 7 then
|
|
print "Coordinates must be in the range 0-7"
|
|
else if x%2 != y%2 then
|
|
print "Invalid coordinates (both must be odd, or both even)"
|
|
else if sign(board[x][y]) != requiredPieceSign then
|
|
print "Invalid coordinates"
|
|
else
|
|
return [x, y]
|
|
end if
|
|
end while
|
|
end function
|
|
|
|
// Evaluate a potential (computer) move.
|
|
evalMove = function(fromX, fromY, toX, toY)
|
|
score = 0
|
|
|
|
// +2 if it promotes this piece
|
|
if toY == 0 and board[fromX][fromY] == -1 then score += 2
|
|
|
|
// +5 if it jumps an opponent's piece
|
|
if abs(fromY-toY) == 2 then score += 5
|
|
|
|
// -2 if the piece is moving away from the top boundary
|
|
if fromY == 7 then score -= 2
|
|
|
|
// +1 for putting the piece against a vertical boundary
|
|
if toX == 0 or toX == 7 then score += 1
|
|
|
|
// check neighboring pieces of the target position
|
|
for c in [-1, 1]
|
|
if toX+c < 0 or toX+c > 7 or toY-1 < 0 then continue
|
|
// +1 for each adjacent friendly piece
|
|
if board[toX+c][toY-1] < 0 then score += 1
|
|
|
|
// -1 for each opponent piece that could now jump this one
|
|
if toX-c >= 0 and toX-c <= 7 and toY+1 <= 7 and board[toX+c][toY-1] > 0 and (
|
|
board[toX-c][toY+1] == 0 or (toX-c == fromX and toY+1 == fromY)) then score -= 2
|
|
end for
|
|
return score
|
|
end function
|
|
|
|
// Consider a possible (computer) move, including whether it's even valid.
|
|
// Return it or the previous best, whichever is better.
|
|
consider = function(fromX, fromY, toX, toY, previousBest)
|
|
|
|
// make sure it's within the bounds of the board
|
|
if toX < 0 or toX > 7 or toY < 0 or toY > 7 then return previousBest
|
|
|
|
// if it's an opponent's piece, consider jumping it instead
|
|
dx = toX - fromX
|
|
if board[toX][toY] > 0 and abs(dx) == 1 then
|
|
dy = toY - fromY
|
|
return consider(fromX, fromY, fromX + dx*2, fromY + dy*2, previousBest)
|
|
end if
|
|
|
|
// if it's a jump, make sure it's over an opponent piece
|
|
if abs(dx) == 2 then
|
|
midX = (fromX + toX)/2; midY = (fromY + toY)/2
|
|
if board[midX][midY] < 1 then return previousBest
|
|
end if
|
|
|
|
// make sure the destination is empty
|
|
if board[toX][toY] then return previousBest
|
|
|
|
// all checks passed; score it, and return whichever is better
|
|
rating = evalMove(fromX, fromY, toX, toY)
|
|
if not previousBest or rating > previousBest.rating then
|
|
newBest = {}
|
|
newBest.fromX = fromX; newBest.fromY = fromY
|
|
newBest.toX = toX; newBest.toY = toY
|
|
newBest.rating = rating
|
|
return newBest
|
|
else
|
|
return previousBest
|
|
end if
|
|
end function
|
|
|
|
// Do the computer's turn
|
|
doComputerTurn = function
|
|
// For each square on the board containing one of my pieces, consider
|
|
// possible moves and keep the best one so far. Start with a step
|
|
// size of 1 (ordinary move), but if we jump, then keep jumping.
|
|
stepSize = 1
|
|
while true
|
|
bestMove = null
|
|
for x in range(0, 7)
|
|
for y in range(0, 7)
|
|
if board[x][y] >= 0 then continue // not my piece
|
|
move = {}
|
|
move.fromPos = [x,y]
|
|
|
|
// Consider forward moves; if it's a king, also consider backward moves
|
|
for dx in [-stepSize, stepSize]
|
|
move.toPos = [dx, -stepSize]
|
|
bestMove = consider(x, y, x+dx, y-stepSize, bestMove)
|
|
if board[x][y] == -2 then bestMove = consider(x, y, x+dx, y+stepSize, bestMove)
|
|
end for
|
|
end for
|
|
end for
|
|
|
|
if not bestMove then
|
|
// No valid move -- if step size is still 1, this means we
|
|
// couldn't find ANY move on our turn, and we have lost.
|
|
// Otherwise, we're done.
|
|
if stepSize == 1 then
|
|
globals.gameOver = true
|
|
globals.winner = 1
|
|
end if
|
|
break
|
|
end if
|
|
|
|
// Do the move, and stop if we did not jump.
|
|
if stepSize == 1 then
|
|
print "From " + bestMove.fromX + " " + bestMove.fromY, ""
|
|
end if
|
|
print " to " + bestMove.toX + " " + bestMove.toY, ""
|
|
|
|
movePiece bestMove.fromX, bestMove.fromY, bestMove.toX, bestMove.toY
|
|
if abs(bestMove.toX - bestMove.fromX) == 1 then break
|
|
stepSize = 2
|
|
end while
|
|
print
|
|
end function
|
|
|
|
// Move one piece (including captures and crowning
|
|
movePiece = function(fromX, fromY, toX, toY)
|
|
piece = board[fromX][fromY]
|
|
board[toX][toY] = piece
|
|
board[fromX][fromY] = 0
|
|
// capture piece jumped over
|
|
if abs(toX - fromX) == 2 then
|
|
board[(fromX+toX)/2][(fromY+toY)/2] = 0
|
|
end if
|
|
// crown (make into a king) a piece that reaches the back row
|
|
if (toY == 7 and piece > 0) or (toY == 0 and piece < 0) then
|
|
board[toX][toY] = 2 * sign(piece)
|
|
end if
|
|
end function
|
|
|
|
// Handle the player's move.
|
|
doPlayerTurn = function
|
|
fromPos = inputPosition("From", 1, true)
|
|
fromX = fromPos[0]; fromY = fromPos[1]
|
|
if fromX < 0 then
|
|
// Player concedes the game.
|
|
globals.gameOver = true
|
|
globals.winner = -1
|
|
return
|
|
end if
|
|
while true
|
|
toPos = inputPosition("To", 0)
|
|
toX = toPos[0]; toY = toPos[1]
|
|
dist = abs(toX - fromX)
|
|
if dist <= 2 and abs(toY - fromY) == dist then break
|
|
end while
|
|
while true
|
|
// Make the move, and continue as long as we have a jump
|
|
movePiece fromX, fromY, toX, toY
|
|
if dist != 2 then break
|
|
|
|
// Prompt for another move, allowing a cancel (negative input).
|
|
fromX = toX; fromY = toX
|
|
toPos = inputPosition("+To", 0, true)
|
|
if toPos[0] < 0 or toPos[1] < 0 then break
|
|
toX = toPos[0]; toY = toPos[1]
|
|
end while
|
|
// If piece has reached the end of the board, crown this piece
|
|
if toY == 7 then board[toX][toY] = 2
|
|
end function
|
|
|
|
// Main loop.
|
|
gameOver = false
|
|
while not gameOver
|
|
doComputerTurn
|
|
if gameOver then break
|
|
printBoard
|
|
doPlayerTurn
|
|
if gameOver then break
|
|
printBoard
|
|
end while
|
|
|
|
print
|
|
if winner > 0 then print "You win." else print "I win."
|