mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2025-12-22 07:10:42 -08:00
Avoid executing code on module level as this prevents importing the module for testing. Especially infinite loops are evil.
175 lines
4.6 KiB
Python
175 lines
4.6 KiB
Python
import sys
|
|
|
|
|
|
class Disk:
|
|
def __init__(self, size):
|
|
self.__size = size
|
|
|
|
def size(self):
|
|
return self.__size
|
|
|
|
def print(self):
|
|
print("[ %s ]" % self.size())
|
|
|
|
|
|
class Tower:
|
|
def __init__(self):
|
|
self.__disks = []
|
|
|
|
def empty(self):
|
|
return len(self.__disks) == 0
|
|
|
|
def top(self):
|
|
if self.empty():
|
|
return None
|
|
else:
|
|
return self.__disks[-1]
|
|
|
|
def add(self, disk):
|
|
if not self.empty():
|
|
t = self.top()
|
|
if disk.size() > t.size():
|
|
raise Exception(
|
|
"YOU CAN'T PLACE A LARGER DISK ON TOP OF A SMALLER ONE, IT MIGHT CRUSH IT!"
|
|
)
|
|
self.__disks.append(disk)
|
|
|
|
def pop(self):
|
|
if self.empty():
|
|
raise Exception("empty pop")
|
|
return self.__disks.pop()
|
|
|
|
def print(self):
|
|
r = "Needle: [%s]" % (", ".join([str(x.size()) for x in self.__disks]))
|
|
print(r)
|
|
|
|
|
|
class Game:
|
|
def __init__(self):
|
|
# use fewer sizes to make debugging easier
|
|
# self.__sizes = [3, 5, 7] # ,9,11,13,15]
|
|
self.__sizes = [3, 5, 7, 9, 11, 13, 15]
|
|
|
|
self.__sizes.sort()
|
|
|
|
self.__towers = []
|
|
self.__moves = 0
|
|
self.__towers = [Tower(), Tower(), Tower()]
|
|
self.__sizes.reverse()
|
|
for size in self.__sizes:
|
|
disk = Disk(size)
|
|
self.__towers[0].add(disk)
|
|
|
|
def winner(self):
|
|
return self.__towers[0].empty() and self.__towers[1].empty()
|
|
|
|
def print(self):
|
|
for t in self.__towers:
|
|
t.print()
|
|
|
|
def moves(self):
|
|
return self.__moves
|
|
|
|
def which_disk(self):
|
|
w = int(input("WHICH DISK WOULD YOU LIKE TO MOVE\n"))
|
|
if w in self.__sizes:
|
|
return w
|
|
raise Exception()
|
|
|
|
def pick_disk(self):
|
|
which = None
|
|
while which is None:
|
|
try:
|
|
which = self.which_disk()
|
|
except Exception:
|
|
print("ILLEGAL ENTRY... YOU MAY ONLY TYPE 3,5,7,9,11,13, OR 15.\n")
|
|
|
|
valids = [t for t in self.__towers if t.top() and t.top().size() == which]
|
|
assert len(valids) in (0, 1)
|
|
if not valids:
|
|
print("THAT DISK IS BELOW ANOTHER ONE. MAKE ANOTHER CHOICE.\n")
|
|
return None
|
|
else:
|
|
assert valids[0].top().size() == which
|
|
return valids[0]
|
|
|
|
def which_tower(self):
|
|
try:
|
|
needle = int(input("PLACE DISK ON WHICH NEEDLE\n"))
|
|
tower = self.__towers[needle - 1]
|
|
except Exception:
|
|
print(
|
|
"I'LL ASSUME YOU HIT THE WRONG KEY THIS TIME. BUT WATCH IT,\nI ONLY ALLOW ONE MISTAKE.\n"
|
|
)
|
|
return None
|
|
else:
|
|
return tower
|
|
|
|
def take_turn(self):
|
|
from_tower = None
|
|
while from_tower is None:
|
|
from_tower = self.pick_disk()
|
|
|
|
to_tower = self.which_tower()
|
|
if not to_tower:
|
|
to_tower = self.which_tower()
|
|
|
|
if not to_tower:
|
|
print("I TRIED TO WARN YOU, BUT YOU WOULDN'T LISTEN.\nBYE BYE, BIG SHOT.\n")
|
|
sys.exit(0)
|
|
|
|
disk = from_tower.pop()
|
|
try:
|
|
to_tower.add(disk)
|
|
self.__moves += 1
|
|
except Exception as err:
|
|
print(err)
|
|
from_tower.add(disk)
|
|
|
|
|
|
def main():
|
|
print(
|
|
"""
|
|
IN THIS PROGRAM, WE SHALL REFER TO DISKS BY NUMERICAL CODE.
|
|
3 WILL REPRESENT THE SMALLEST DISK, 5 THE NEXT SIZE,
|
|
7 THE NEXT, AND SO ON, UP TO 15. IF YOU DO THE PUZZLE WITH
|
|
2 DISKS, THEIR CODE NAMES WOULD BE 13 AND 15. WITH 3 DISKS
|
|
THE CODE NAMES WOULD BE 11, 13 AND 15, ETC. THE NEEDLES
|
|
ARE NUMBERED FROM LEFT TO RIGHT, 1 TO 3. WE WILL
|
|
START WITH THE DISKS ON NEEDLE 1, AND ATTEMPT TO MOVE THEM
|
|
TO NEEDLE 3.
|
|
|
|
GOOD LUCK!
|
|
|
|
"""
|
|
)
|
|
|
|
game = Game()
|
|
while True:
|
|
game.print()
|
|
|
|
game.take_turn()
|
|
|
|
if game.winner():
|
|
print(
|
|
"CONGRATULATIONS!!\nYOU HAVE PERFORMED THE TASK IN %s MOVES.\n"
|
|
% game.moves()
|
|
)
|
|
while True:
|
|
yesno = input("TRY AGAIN (YES OR NO)\n")
|
|
if yesno.upper() == "YES":
|
|
game = Game()
|
|
break
|
|
elif yesno.upper() == "NO":
|
|
print("THANKS FOR THE GAME!\n")
|
|
sys.exit(0)
|
|
else:
|
|
print("'YES' OR 'NO' PLEASE\n")
|
|
elif game.moves() > 128:
|
|
print("SORRY, BUT I HAVE ORDERS TO STOP IF YOU MAKE MORE THAN 128 MOVES.")
|
|
sys.exit(0)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|