This is the FINAL version of what I'm submitting.

This is a version of minesweeper. You win by
isolating every mine, which is the same as
revealing all numbered spaces. There is no flag,
so you have to keep track of, or figure out,
what's revealed. The game ends if you win as
described above or you lose by selecting a mine.

I have playtested this and confirmed that the
game ends in a win by meeting the above criterion.
I also (not intentionally) confirmed you lose if
you select a mine. I did the latter far too many
times...

Things to note:
-I have the game in debug mode so that
instructions appear.
-There is no score. I don't think there's a
score in minesweeper. You either survive the mines
or die.
-I have not implemented flagging potential
locations of mines. You can technically play
without them, so I don't think you lose out on
any of the core gameplay.
-I'm not sure why, but when the game ends, some
of the mines don't show up and some of the
revealed numbers disappear.
-Because two displayed agents cannot occupy the
same space, navigating with the cursor over mines
or numbered squares may require pressing an arrow
key multiple times. (I tried to not display the
mines and then display them only when revealing
spaces, but the revealing algorithm didn't work
when I tried it like that. I promise I spent
hours trying to get it to work before settling on
this current iteration.)
-You can lose on the first move...
This commit is contained in:
Cory 2024-05-19 04:06:01 -04:00
parent 8e54c5fe12
commit 374952d65a
9 changed files with 128 additions and 56 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -2,6 +2,7 @@
# ------------
# By Cory
# This module defines a cursor agent class.
class Cursor:
name = "cursor"
character = 'O'
@ -11,8 +12,10 @@ class Cursor:
self.position = position
def handle_keystroke(self, keystroke, game):
'''
Move the cursor using arrow keys.
'''
x, y = self.position
if keystroke.name in ("KEY_LEFT", "KEY_RIGHT", "KEY_UP", "KEY_DOWN"):
if keystroke.name == "KEY_LEFT":
new_position = (x - 1, y)
@ -25,9 +28,10 @@ class Cursor:
if game.on_board(new_position):
if game.is_empty(new_position):
self.position = new_position
game.log("The cursor is at " + str(self.position))
for i in range(10):
game.log("mine"+str(i) + " is located at " + str(game.get_agent_by_name("mine"+str(i)).position))
def hide(self):
'''
This is called because I was calling this method based on an agent's location, but I don't need the
cursor to do anything.
'''
pass

View File

@ -14,46 +14,28 @@ class FreeSpace:
self.position = position
self.width , self.height = position
def handle_keystroke(self, keystroke, game):
if keystroke.name in ("KEY_ENTER"):
if not game.is_empty(self.position):
if self.revealed == False:
board_width,board_height = game.board_size
for i in range(10):
game.get_agent_by_name("mine"+str(i)).show()
for i in range(board_width):
for j in range(board_height):
try:
game.get_agent_by_name("freespace"+str(i)+str(j)).show()
except:
pass
self.reveal(game)
for i in range(board_width):
for j in range(board_height):
try:
game.get_agent_by_name("freespace"+str(i)+str(j)).hide()
except:
pass
for i in range(10):
game.get_agent_by_name("mine"+str(i)).hide()
def name_me(self, named):
'''
Give a free space a name.'''
self.name = named
def show(self):
'''
Used to turn display on. Necessary to be able to call upon a space based on its position.
'''
self.display = True
def hide(self):
'''
Used to hide free spaces only if they have not already been revealed.
'''
if self.revealed == False:
self.display = False
def show(self):
self.display = True
def play_turn(self, game):
if (not game.is_empty(self.position)) and self.revealed and self.display == True:
self.display = False
elif game.is_empty(self.position) and self.revealed and self.neighbors > 0 and self.display == False:
self.display = True
def name_me(self, named):
self.name = named
def check_neighbors(self,game):
'''
Used to determine the number that should be shown when the space is revealed. Counts neighboring mines.
'''
names_of_mines = ["mine0","mine1","mine2","mine3","mine4","mine5","mine6","mine7","mine8","mine9"]
if game.on_board((self.width + 1,self.height)):
if len(game.get_agents_by_position()[(self.width + 1,self.height)]) != 0:
@ -90,7 +72,54 @@ class FreeSpace:
if self.neighbors > 0:
self.character = str(self.neighbors)
def play_turn(self, game):
'''
Make sure a number is hidden when the cursor is over it. This is needed to allow the cursor to move
over a spot where a number is.
'''
if (not game.is_empty(self.position)) and self.revealed and self.display == True:
self.display = False
elif game.is_empty(self.position) and self.revealed and self.neighbors > 0 and self.display == False:
self.display = True
def handle_keystroke(self, keystroke, game):
'''
When a space without a mine is selected, use a recursive algorithm defined below to reveal spaces.
Then, check if all non-mine spaces have been revealed; if so, the game has been won.
'''
if keystroke.name in ("KEY_ENTER"):
if not game.is_empty(self.position):
if self.revealed == False:
board_width,board_height = game.board_size
for i in range(board_width):
for j in range(board_height):
try:
game.get_agent_by_name("freespace"+str(i)+str(j)).show()
except:
pass
self.reveal(game)
win = True
for i in range(board_width):
for j in range(board_height):
if len(game.get_agents_by_position()[(i,j)]) != 0:
if not game.get_agents_by_position()[i,j][0].revealed:
win = False
if win == True:
game.log("You successfully isolated every mine! Congratulations! You've won!")
game.end()
for i in range(board_width):
for j in range(board_height):
try:
game.get_agent_by_name("freespace"+str(i)+str(j)).hide()
except:
pass
def reveal(self,game):
'''
Recursive algorithm to reveal squares. Reveals itself and then if it has no neighboring mines,
it asks all 8 of its neighbors (assuming none are off the map) to reveal themselves.
'''
self.revealed = True
if self.neighbors == 0:
if game.on_board((self.width + 1,self.height)):

52
mine.py
View File

@ -10,29 +10,61 @@ class Mine:
def __init__(self, position):
self.position = position
def name_me(self, named):
'''
Assign a name to a given mine.
'''
self.name = named
def handle_keystroke(self, keystroke, game):
'''
This ends the game if enter is pressed while the cursor is over a mine.
'''
if keystroke.name in ("KEY_ENTER"):
if game.get_agent_by_name("cursor").position == self.position:
for i in range(10):
game.get_agent_by_name("mine"+str(i)).character = 'M'
game.get_agent_by_name("cursor").character = 'M'
game.log("You hit a mine! Game over.")
game.end()
def play_turn(self, game):
'''
Make sure the mine is hidden when the cursor is over it. This is needed to allow the cursor to move
over a spot where a mine is.
'''
if (not game.is_empty(self.position)):
self.display = False
elif game.is_empty(self.position):
self.display = True
def hide(self):
pass
def name_me(self, named):
self.name = named
def check_neighbors(self,game):
pass
def reveal(self):
'''
This is called because I was calling this method based on an agent's location, but I don't need the
mine to do anything.
'''
pass
def show(self):
pass
'''
This is called because I was calling this method based on an agent's location, but I don't need the
mine to do anything.
'''
pass
def hide(self):
'''
This is called because I was calling this method based on an agent's location, but I don't need the
mine to do anything.
'''
pass
def check_neighbors(self,game):
'''
This is called because I was calling this method based on an agent's location, but I don't need the
mine to do anything.
'''
pass

View File

@ -1,12 +1,12 @@
# minesweeper_game.py
# ------------
# By Cory
# This class implements a simple minesweeper game on a 9x9 grid.
# This class implements a simple minesweeper game on a 9x9 grid with 10 mines.
from retro.game import Game
from spawner import Spawner
board_size = (9, 9)
spawner = Spawner(board_size)
game = Game([spawner], {"score": 0}, board_size=board_size,debug=True)
game = Game([spawner], {"score": 0}, board_size=board_size,debug=True) # debug is on so you can see the instructions :)
game.play()

View File

@ -16,26 +16,26 @@ class Spawner:
self.board_width, self.board_height = width, height
def play_turn(self, game):
# On the first turn we will spawn everything.
'''
Creates all other agents and prints instructions when the game begins.
'''
if game.turn_number == 1:
# First spawn 10 mines
for i in range(10):
mine = Mine(((randint(0, self.board_width - 1)),(randint(0, self.board_height - 1))))
mine.name_me("mine"+str(i))
while not game.is_empty(mine.position):
while not game.is_empty(mine.position): # Ensure mines are not in the same location
mine = Mine(((randint(0, self.board_width - 1)),(randint(0, self.board_height - 1))))
mine.name_me("mine"+str(i))
game.log(str(mine.name) + " is located at " + str(mine.position))
game.add_agent(mine)
# Then spawn free spaces everywhere
# Then spawn free spaces everywhere there is not a mine.
for i in range(self.board_width):
for j in range(self.board_height):
if game.is_empty((i,j)):
free_space = FreeSpace((i,j))
free_space.name_me("freespace"+str(i)+str(j))
game.add_agent(free_space)
# Now ask all free spaces to keep track of their neighbors that are mines
# and all of the mines and free spaces to hide themselves.
# Now ask all free spaces to keep track of their neighbors that are mines and hide themselves.
for i in range(self.board_width):
for j in range(self.board_height):
if len(game.get_agents_by_position()[(i,j)]) != 0:
@ -44,3 +44,10 @@ class Spawner:
# Now create a cursor.
cursor = Cursor((self.board_width - 1, self.board_height - 1))
game.add_agent(cursor)
# Print instructions.
game.log("To move, use the arrow keys. You may have to")
game.log("press an arrow key more than once to move.")
game.log("Please make sure the cursor is where you want it")
game.log("before selecting a space. To select a space, ")
game.log("press return. The game is over when all spots")
game.log("with mines are surrounded by numbers.")