mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2025-12-12 07:40:50 -08:00
Added MiniScript version of 23_Checkers.
This commit is contained in:
16
00_Alternate_Languages/23_Checkers/MiniScript/README.md
Normal file
16
00_Alternate_Languages/23_Checkers/MiniScript/README.md
Normal file
@@ -0,0 +1,16 @@
|
||||
Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html).
|
||||
|
||||
Conversion to [MiniScript](https://miniscript.org).
|
||||
|
||||
Ways to play:
|
||||
|
||||
1. Command-Line MiniScript:
|
||||
Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as:
|
||||
|
||||
miniscript checkers.ms
|
||||
|
||||
2. Mini Micro:
|
||||
Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the BASIC program. Then, at the Mini Micro command prompt, enter:
|
||||
|
||||
load "checkers"
|
||||
run
|
||||
243
00_Alternate_Languages/23_Checkers/MiniScript/checkers.ms
Normal file
243
00_Alternate_Languages/23_Checkers/MiniScript/checkers.ms
Normal file
@@ -0,0 +1,243 @@
|
||||
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."
|
||||
@@ -24,5 +24,6 @@ should be more readable.
|
||||
- If the computer moves a checker to the bottom row, it promotes, but
|
||||
leaves the original checker in place. (See line 1240)
|
||||
- Human players may move non-kings as if they were kings. (See lines 1590 to 1810)
|
||||
- Human players are not required to jump if it is possible.
|
||||
- Human players are not required to jump if it is possible, and may make a number
|
||||
other illegal moves (jumping their own pieces, jumping empty squares, etc.).
|
||||
- Curious writing to "I" variable without ever reading it. (See lines 1700 and 1806)
|
||||
|
||||
Reference in New Issue
Block a user