mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2025-12-21 23:00:43 -08:00
175 lines
6.2 KiB
Python
175 lines
6.2 KiB
Python
from enum import Enum
|
|
|
|
|
|
class WinOptions(Enum):
|
|
Undefined = 0
|
|
TakeLast = 1
|
|
AvoidLast = 2
|
|
|
|
|
|
class StartOptions(Enum):
|
|
Undefined = 0
|
|
ComputerFirst = 1
|
|
PlayerFirst = 2
|
|
|
|
|
|
def PrintIntro():
|
|
"""Prints out the introduction and rules for the game."""
|
|
print("BATNUM".rjust(33, " "))
|
|
print("CREATIVE COMPUTING MORRISSTOWN, NEW JERSEY".rjust(15, " "))
|
|
print()
|
|
print()
|
|
print()
|
|
print("THIS PROGRAM IS A 'BATTLE OF NUMBERS' GAME, WHERE THE")
|
|
print("COMPUTER IS YOUR OPPONENT.")
|
|
print()
|
|
print("THE GAME STARTS WITH AN ASSUMED PILE OF OBJECTS. YOU")
|
|
print("AND YOUR OPPONENT ALTERNATELY REMOVE OBJECTS FROM THE PILE.")
|
|
print("WINNING IS DEFINED IN ADVANCE AS TAKING THE LAST OBJECT OR")
|
|
print("NOT. YOU CAN ALSO SPECIFY SOME OTHER BEGINNING CONDITIONS.")
|
|
print("DON'T USE ZERO, HOWEVER, IN PLAYING THE GAME.")
|
|
print("ENTER A NEGATIVE NUMBER FOR NEW PILE SIZE TO STOP PLAYING.")
|
|
print()
|
|
return
|
|
|
|
|
|
def GetParams():
|
|
"""This requests the necessary parameters to play the game.
|
|
|
|
Returns a set with the five game parameters:
|
|
pileSize - the starting size of the object pile
|
|
minSelect - minimum selection that can be made on each turn
|
|
maxSelect - maximum selection that can be made on each turn
|
|
startOption - 1 if the computer is first
|
|
or 2 if the player is first
|
|
winOption - 1 if the goal is to take the last object
|
|
or 2 if the goal is to not take the last object
|
|
"""
|
|
pileSize = 0
|
|
winOption = WinOptions.Undefined
|
|
minSelect = 0
|
|
maxSelect = 0
|
|
startOption = StartOptions.Undefined
|
|
|
|
while pileSize < 1:
|
|
pileSize = int(input("ENTER PILE SIZE "))
|
|
while winOption == WinOptions.Undefined:
|
|
winOption = int(input("ENTER WIN OPTION - 1 TO TAKE LAST, 2 TO AVOID LAST: "))
|
|
while minSelect < 1 or maxSelect < 1 or minSelect > maxSelect:
|
|
(minSelect, maxSelect) = (
|
|
int(x) for x in input("ENTER MIN AND MAX ").split(" ")
|
|
)
|
|
while startOption == StartOptions.Undefined:
|
|
startOption = int(input("ENTER START OPTION - 1 COMPUTER FIRST, 2 YOU FIRST "))
|
|
return (pileSize, minSelect, maxSelect, startOption, winOption)
|
|
|
|
|
|
def PlayerMove(pileSize, minSelect, maxSelect, startOption, winOption):
|
|
"""This handles the player's turn - asking the player how many objects
|
|
to take and doing some basic validation around that input. Then it
|
|
checks for any win conditions.
|
|
|
|
Returns a boolean indicating whether the game is over and the new pileSize."""
|
|
playerDone = False
|
|
while not playerDone:
|
|
playerMove = int(input("YOUR MOVE "))
|
|
if playerMove == 0:
|
|
print("I TOLD YOU NOT TO USE ZERO! COMPUTER WINS BY FORFEIT.")
|
|
return (True, pileSize)
|
|
if playerMove > maxSelect or playerMove < minSelect:
|
|
print("ILLEGAL MOVE, REENTER IT")
|
|
continue
|
|
pileSize = pileSize - playerMove
|
|
playerDone = True
|
|
if pileSize <= 0:
|
|
if winOption == WinOptions.AvoidLast:
|
|
print("TOUGH LUCK, YOU LOSE.")
|
|
else:
|
|
print("CONGRATULATIONS, YOU WIN.")
|
|
return (True, pileSize)
|
|
return (False, pileSize)
|
|
|
|
|
|
def ComputerPick(pileSize, minSelect, maxSelect, startOption, winOption):
|
|
"""This handles the logic to determine how many objects the computer
|
|
will select on its turn.
|
|
"""
|
|
q = pileSize - 1 if winOption == WinOptions.AvoidLast else pileSize
|
|
c = minSelect + maxSelect
|
|
computerPick = q - (c * int(q / c))
|
|
if computerPick < minSelect:
|
|
computerPick = minSelect
|
|
if computerPick > maxSelect:
|
|
computerPick = maxSelect
|
|
return computerPick
|
|
|
|
|
|
def ComputerMove(pileSize, minSelect, maxSelect, startOption, winOption):
|
|
"""This handles the computer's turn - first checking for the various
|
|
win/lose conditions and then calculating how many objects
|
|
the computer will take.
|
|
|
|
Returns a boolean indicating whether the game is over and the new pileSize."""
|
|
# First, check for win conditions on this move
|
|
# In this case, we win by taking the last object and
|
|
# the remaining pile is less than max select
|
|
# so the computer can grab them all and win
|
|
if winOption == WinOptions.TakeLast and pileSize <= maxSelect:
|
|
print(f"COMPUTER TAKES {pileSize} AND WINS.")
|
|
return (True, pileSize)
|
|
# In this case, we lose by taking the last object and
|
|
# the remaining pile is less than minsize and the computer
|
|
# has to take all of them.
|
|
if winOption == WinOptions.AvoidLast and pileSize <= minSelect:
|
|
print(f"COMPUTER TAKES {minSelect} AND LOSES.")
|
|
return (True, pileSize)
|
|
|
|
# Otherwise, we determine how many the computer selects
|
|
currSel = ComputerPick(pileSize, minSelect, maxSelect, startOption, winOption)
|
|
pileSize = pileSize - currSel
|
|
print(f"COMPUTER TAKES {currSel} AND LEAVES {pileSize}")
|
|
return (False, pileSize)
|
|
|
|
|
|
def PlayGame(pileSize, minSelect, maxSelect, startOption, winOption):
|
|
"""This is the main game loop - repeating each turn until one
|
|
of the win/lose conditions is met.
|
|
"""
|
|
gameOver = False
|
|
# playersTurn is a boolean keeping track of whether it's the
|
|
# player's or computer's turn
|
|
playersTurn = startOption == StartOptions.PlayerFirst
|
|
|
|
while not gameOver:
|
|
if playersTurn:
|
|
(gameOver, pileSize) = PlayerMove(
|
|
pileSize, minSelect, maxSelect, startOption, winOption
|
|
)
|
|
playersTurn = False
|
|
if gameOver:
|
|
return
|
|
if not playersTurn:
|
|
(gameOver, pileSize) = ComputerMove(
|
|
pileSize, minSelect, maxSelect, startOption, winOption
|
|
)
|
|
playersTurn = True
|
|
|
|
return
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
pileSize = 0
|
|
minSelect = 0
|
|
maxSelect = 0
|
|
# 1 = to take last, 2 = to avoid last
|
|
winOption = 0
|
|
# 1 = computer first, 2 = user first
|
|
startOption = 0
|
|
|
|
while True:
|
|
PrintIntro()
|
|
(pileSize, minSelect, maxSelect, startOption, winOption) = GetParams()
|
|
# Just keep playing the game until the user kills it with ctrl-C
|
|
PlayGame(pileSize, minSelect, maxSelect, startOption, winOption)
|