Skip to content

Commit

Permalink
The game field is generated only after first click
Browse files Browse the repository at this point in the history
Also switching to flags is not disallowed until you make first click
  • Loading branch information
MasterGroosha committed Sep 4, 2021
1 parent 894465d commit cfce663
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 10 deletions.
17 changes: 15 additions & 2 deletions bot/handlers/callbacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.exc import IntegrityError

from bot.minesweeper.game import get_newgame_data, untouched_cells_count, all_flags_match_bombs, make_text_table
from bot.minesweeper.game import (get_fake_newgame_data, untouched_cells_count, all_flags_match_bombs,
make_text_table, get_real_game_data)
from bot.minesweeper.states import ClickMode, CellMask
from bot.keyboards.kb_minefield import make_keyboard_from_minefield
from bot.cbdata import cb_newgame, cb_click, cb_switch_mode, cb_switch_flag, cb_ignore
Expand Down Expand Up @@ -69,7 +70,7 @@ async def callback_newgame(call: types.CallbackQuery, state: FSMContext, callbac
bombs = int(callback_data.get("bombs"))

game_id = str(uuid4())
newgame_dict = {"game_id": game_id, "game_data": get_newgame_data(size, bombs)}
newgame_dict = {"game_id": game_id, "game_data": get_fake_newgame_data(size, bombs)}
await state.set_data(newgame_dict)
await call.message.edit_text(
f"You're currently playing <b>{size}×{size}</b> field, <b>{bombs}</b> bombs",
Expand All @@ -90,6 +91,14 @@ async def callback_open_square(call: types.CallbackQuery, state: FSMContext,
x = int(callback_data["x"])
y = int(callback_data["y"])

# If this is the first click, it's time to generate the real game field
if game_data["initial"] is True:
cells = get_real_game_data(game_data["size"], game_data["bombs"], (x, y))
game_data["cells"] = cells
game_data["initial"] = False
else:
cells = game_data.get("cells")

# This cell contained a bomb
if cells[x][y]["value"] == "*":
cells[x][y]["mask"] = CellMask.BOMB
Expand Down Expand Up @@ -143,6 +152,10 @@ async def switch_click_mode(call: types.CallbackQuery, state: FSMContext, callba
game_data = fsm_data.get("game_data", {})
cells = game_data.get("cells")

if game_data["initial"] is True:
await call.answer(show_alert=True, text="You can only place flags after first click!")
return

game_data["current_mode"] = int(callback_data["new_mode"])
await state.update_data(game_data=game_data)

Expand Down
20 changes: 14 additions & 6 deletions bot/minesweeper/game.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,36 @@
from typing import Dict, List
from typing import Dict, List, Tuple

from texttable import Texttable

from bot.minesweeper.generators import generate_custom
from bot.minesweeper.generators import generate_custom, generate_square_field
from bot.minesweeper.states import CellMask, ClickMode


def get_newgame_data(size: int, bombs: int) -> Dict:
def get_fake_newgame_data(size: int, bombs: int) -> Dict:
"""
Prepares a new game dictionary
:param size: field size (a field is a size x size square)
:param bombs: number of bombs to place
:return: a dictionary with field data for a new game
"""
result = {"current_mode": ClickMode.CLICK, "size": size, "bombs": bombs}
field = generate_custom(size, bombs)
result = {"current_mode": ClickMode.CLICK, "size": size, "bombs": bombs, "initial": True}
field = generate_square_field(size)
for x in range(size):
for y in range(size):
field[x][y] = {"value": field[x][y], "mask": 0, "x": x, "y": y}
field[x][y] = {"value": field[x][y], "mask": CellMask.HIDDEN, "x": x, "y": y}
result["cells"] = field
return result


def get_real_game_data(size: int, bombs: int, predefined: Tuple[int, int]) -> List[List[Dict]]:
field = generate_custom(size, bombs, predefined)
for x in range(size):
for y in range(size):
field[x][y] = {"value": field[x][y], "mask": CellMask.HIDDEN, "x": x, "y": y}
return field


def untouched_cells_count(cells: List[List[Dict]]) -> int:
"""
Counts the number of "untouched" cells: those which status is HIDDEN
Expand Down
6 changes: 4 additions & 2 deletions bot/minesweeper/generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,15 @@ def __find_neighbours(x: int, y: int, size: int) -> List[Tuple]:
return result


def generate_custom(size: int, bombs: int) -> List[List]:
def generate_custom(size: int, bombs: int, predefined: Tuple[int, int]) -> List[List]:
"""
Generates custom square field with bombs (*).
If cell contains a bomb, it has "*" value.
Otherwise, it contains a number of adjacent bomb cells.
:param size: a single dimension of a field
:param bombs: bombs count for this field
:param predefined: coordinates of cell, which MUST be free of bombs
:return: an array of arrays of cells.
"""
field = generate_square_field(size)
Expand All @@ -82,7 +83,8 @@ def generate_custom(size: int, bombs: int) -> List[List]:
x = randint(0, size-1)
y = randint(0, size-1)

if field[x][y] == "*":
# Do not place a bomb on another bomb or on predefined place
if (x == predefined[0] and y == predefined[1]) or field[x][y] == "*":
continue
field[x][y] = "*"
neighbours = __find_neighbours(x, y, size)
Expand Down

0 comments on commit cfce663

Please sign in to comment.