DeTrapper
Jump to navigation
Jump to search
DeTrapper žaidimas
Ši programa demonstruoja, kaip susikurti klasikinį minisweper stiliaus žaidimą, kuriame spaudinėjant ant langelių reikia suprasti, kuriuose langeliuose yra minos. Kiekviename neužminuotame langelyje būna arba skaičius, kuris nurodo šalia esančių minų kiekį arba tuščias (saugus plotas).
Šioje programėlėje panaudoti keli skirtingi vaizdo failai ir garsai, kurie atsiranda priklausomai nuo atlikto veiksmo.
Žaisti DeTrapper
Žaidimui reikalingi failai
Visus garsus ir vaizdus panaudotus šiame žaidime galima atsisiųsti čia.
DeTrapper žaidimo kodas
reikalinga angis
reikalingas žaidimas
import random
class TextBoard:
def __init__(self, x, y, size, color):
self.__color = color
self.__text = žaidimas.Tekstas('')
self.__text.atsirask (x, y)
self.__text.spalva(self.__color)
self.__text.dydis(size)
self.__text.hide()
def show(self):
self.__text.show()
def hide(self):
self.__text.hide()
def set_color(self, color):
self.__color = color
self.__text.spalva(color)
def update_text(self, message):
self.__text.tekstas(message)
class TimerBoard:
def __init__(self, x, y, sound_player = None):
spalva = '#AA0000'
dydis = 20
self.TIME_LIMIT = 999
self.__x = x
self.__y = y
self.seconds = 0
self.time_text = žaidimas.Tekstas('■■■')
self.time_text.spalva(spalva)
self.time_text.dydis(dydis)
self.text_stopped = žaidimas.Tekstas('000')
self.text_stopped.spalva(spalva)
self.text_stopped.dydis(dydis)
self.time_label = žaidimas.Tekstas('Time:')
self.time_label.spalva(spalva)
self.time_label.dydis(dydis - 4)
# self.tekstas_stopped.hide()
self.is_running = False
self.timer_clock = None
self.__sfx = sound_player
def __get_text(self):
return str(self.seconds).zfill(3)
def __update(self):
if self.is_running:
self.seconds += 1
if self.seconds >= self.TIME_LIMIT:
self.seconds = self.TIME_LIMIT
self.stop_timer()
self.time_text.tekstas(self.__get_text())
self.text_stopped.hide()
if self.__sfx is not None:
self.__sfx.play_sound('timer-tick')
self.time_text.show(self.__x + 42, self.__y)
else:
self.time_text.hide()
self.text_stopped.show(self.__x + 42, self.__y)
def reset(self):
self.seconds = 0
self.stop_timer()
self.display()
def display(self):
self.time_label.show(self.__x, self.__y + 2)
if self.is_running:
self.text_stopped.hide()
self.time_text.tekstas(self.__get_text())
self.time_text.show(self.__x + 42, self.__y)
else:
self.time_text.hide()
self.text_stopped.tekstas(self.__get_text())
self.text_stopped.show(self.__x + 42, self.__y)
def start_timer(self):
if not self.is_running:
if self.timer_clock == None:
self.timer_clock = angis.Taimeris(self.__update, 1)
self.is_running = True
self.display()
def stop_timer(self):
if self.is_running:
self.is_running = False
# self.timer_clock.stok()
self.text_stopped.tekstas(self.__get_text())
class CoordsConveter:
def __init__(self):
self.areas = [
('tile', 51, 51, 500, 500),
('menu_m', 50, 10, 150, 30),
('menu_r', 250, 10, 310, 30)
]
def calc_click_result(self, x, y):
tile_col = x // 50
tile_row = y // 50
for area in self.areas:
if area[1] <= x and x <= area[3] and area[2] <= y and y <= area[4]:
return (area[0], tile_col, tile_row)
return ('none', 0, 0)
class SoundPlayer:
def __init__(self):
self.__sfx = { }
self.__is_muted = False
def preload_sounds(self):
sfx_list = ['trap-activated', 'timer-tick', 'slash', 'flag-in', 'flag-out', 'multi-clear', 'congrats', 'try-again']
sfx_files = [f'garsai/{sfx}.mp3' for sfx in sfx_list]
angis.naudosiuFailus(sfx_files)
for sfx in sfx_list:
self.__sfx[sfx] = angis.Garsas(f"garsai/{sfx}.mp3")
def play_sound(self, sfx):
if (not self.__is_muted) and (sfx in self.__sfx):
self.__sfx[sfx].grokFone()
def toggle_sound(self):
self.__is_muted = not self.__is_muted
return self.__is_muted
class TrapField:
def __init__(self, sound_player):
self.COLOR_MENU_TITLE = '#663300'
self.COLOR_MENU_TEXT = '#884400'
self.__border_parts = [ [1, 2, 3], [4, 14, 6], [7, 8, 9] ]
self.__closed_tile = 'pav/traps/closed-field.png'
self.__flagged_tile = 'pav/traps/flagged-field.png'
self.__hint_files = [f'pav/traps/open-field-{hint}.png' for hint in range(10)]
self.__board_tiles = []
self.__sfx = sound_player
self.__help_texts = []
self.__is_help_visible = False
def preload_pictures(self):
gfx_files = [f'pav/plyt/{self.__border_parts[i][j]}.png' for i in range(3) for j in range(3)]
gfx_files.append(self.__closed_tile)
gfx_files.append(self.__flagged_tile)
for hint in self.__hint_files:
gfx_files.append(hint)
angis.naudosiuFailus(gfx_files)
def preload_sounds(self):
self.__sfx.preload_sounds()
def draw_board(self, width, height, scene):
# border with empty field
for y in range(height + 2):
row = 0
if 0 == y:
row = 0
elif y < height + 1:
row = 1
else:
row = 2
row_tiles = []
for x in range(width + 2):
col = 0
if 0 == x:
col = 0
elif x < width + 1:
col = 1
else:
col = 2
tile = scene.sukurkPlytelę(f'pav/plyt/{self.__border_parts[row][col]}.png', x, y)
row_tiles.append(tile)
self.__board_tiles.append(row_tiles)
# covered mine field
for y in range(1, height + 1):
for x in range(1, width + 1):
self.__board_tiles[y][x].naudokPaveiksliuką(self.__closed_tile)
def get_closed_pic(self):
return self.__closed_tile
def update_tile(self, x, y, value):
if 0 <= value <= 8:
self.__board_tiles[y][x].background(self.__hint_files[value])
elif value == 9:
# self.__sfx.play_sound('trap-activated')
self.__board_tiles[y][x].background(self.__hint_files[9])
elif value == -1:
# this place is not suitable for playing sound, cause on restart it happens <=81 times
# self.__sfx.play_sound('flag-out')
self.__board_tiles[y][x].background(self.__closed_tile)
elif value == 10:
# self.__sfx.play_sound('flag-in')
self.__board_tiles[y][x].background(self.__flagged_tile)
def toggle_help(self, width, height):
if not self.__is_help_visible:
# empty field
for y in range(1, height + 1):
for x in range(1, width + 1):
self.__board_tiles[y][x].background(f'pav/plyt/{self.__border_parts[1][1]}.png')
if len(self.__help_texts) == 0:
self.__help_texts = [
TextBoard(100, 80, 24, self.COLOR_MENU_TITLE),
TextBoard(100, 120, 18, self.COLOR_MENU_TEXT),
TextBoard(100, 150, 18, self.COLOR_MENU_TEXT),
TextBoard(100, 180, 18, self.COLOR_MENU_TEXT),
TextBoard(100, 210, 18, self.COLOR_MENU_TEXT),
TextBoard(100, 240, 18, self.COLOR_MENU_TEXT),
TextBoard(100, 260, 18, self.COLOR_MENU_TEXT)
]
self.__help_texts[0].update_text('===== Help =====')
self.__help_texts[1].update_text('[H] - toggle help: on/off')
self.__help_texts[2].update_text('[M] - toggle mode: dig /flag')
self.__help_texts[3].update_text('[R] - restart game')
self.__help_texts[4].update_text('[S] - toggle sound: on/off')
self.__help_texts[5].update_text('You can also use mouse to change mode')
self.__help_texts[6].update_text('or restart the game')
for txt in self.__help_texts:
txt.show()
else:
for txt in self.__help_texts:
txt.hide()
self.__is_help_visible = not self.__is_help_visible
return self.__is_help_visible
class DeTrapperGame:
def __init__(self, width=9, height=9, traps_count=10):
self.COLOR_DEFAULT = '#333333'
self.COLOR_FOCUS_RESTART = '#3333AA'
self.__width = width
self.__height = height
self.__traps_count = traps_count
self.__sfx = SoundPlayer()
self.__gfx = TrapField(self.__sfx)
self.__scene = žaidimas.duokSceną()
self.__converter = CoordsConveter()
# field click mode: dig / flag
self.__mode_display = TextBoard(50, 10, 16, self.COLOR_DEFAULT)
self.__restart_display = TextBoard(250, 10, 16, self.COLOR_DEFAULT)
self.__restart_display.update_text('[R]estart')
self.__time_display = TimerBoard(420, 10, self.__sfx)
self.__info_display = TextBoard(50, 515, 16, self.COLOR_DEFAULT)
self.MODES = [ 'dig', 'flag' ]
self.__init_game()
self.__draw_init_board()
self.__started = False
self.__is_help_visible = False
def __init_game(self):
self.__mode = 0
self.__update_mode_display()
self.__info_display.set_color(self.COLOR_DEFAULT)
self.__info_display.update_text('[H] - toggle help on/off')
self.__info_display.show()
self.__is_help_visible = False
self.ended = False
self.won = False
self.__board = []
self.view = []
for y in range(self.__height + 2):
self.__board.append([])
self.view.append([])
for x in range(self.__width + 2):
self.__board[y].append(0)
self.view[y].append(0)
self.__randomize_traps()
self.__add_hints()
def __randomize_traps(self):
n = 0
while n < self.__traps_count:
x = random.randint(1, self.__width)
y = random.randint(1, self.__height)
if self.__board[y][x] == 0:
self.__board[y][x] = 9
n = n + 1
def __add_hints(self):
for y in range(self.__height + 2):
for x in range(self.__width + 2):
if self.__board[y][x] == 9:
for dy in range(-1, 2):
for dx in range(-1, 2):
if dy == 0 and dx == 0:
continue
if self.__board[y+dy][x+dx] != 9:
self.__board[y+dy][x+dx] += 1
def __update_mode_display(self):
if self.__mode == 0:
self.__mode_display.set_color(self.COLOR_DEFAULT)
else:
self.__mode_display.set_color(self.COLOR_FOCUS_RESTART)
mode_name = self.MODES[self.__mode]
self.__mode_display.update_text(f'[M]ode: {mode_name}')
def toggle_mode(self):
self.__mode = (self.__mode + 1) % 2
self.__update_mode_display()
# debug
# self.show_message(f'New mode: {self.__mode}')
def toggle_flag(self, x, y):
if self.view[y][x] == 0:
self.view[y][x] = 2
self.__sfx.play_sound('flag-in')
return 10
# self.__gfx.update_tile(x, y, 10)
elif self.view[y][x] == 2:
self.view[y][x] = 0
self.__sfx.play_sound('flag-out')
return -1
# self.__gfx.update_tile(x, y, -1)
return self.__board[y][x]
def toggle_sound(self):
self.__sfx.toggle_sound()
def show_message(self, text):
self.__info_display.update_text(text)
def open_field(self, x, y):
if self.ended:
return
# when it's a flagged field or already opened - do nothing
if x < 1 or self.__width < x or y < 1 or self.__height < y:
# self.show_message(f'Out of bounds: {x}, {y} ')
return
if (self.__mode == 0 and self.view[y][x]==2) or self.view[y][x] == 1:
# self.show_message(f"Can't click: ({x},{y}) - m={self.__mode}, v={self.view[y][x]}")
return
# self.show_message(f'Click: {x}, {y}, m={self.__mode}, v={self.view[y][x]}')
if self.__mode == 1:
value = self.toggle_flag(x, y)
self.update_board(x, y, False)
return
if self.__board[y][x] == 0:
# recursion to open all zero fields
# does not work with bigger boards due to deepness limit!
self.__sfx.play_sound('multi-clear')
self.__clear_empty_zone(x, y)
else:
if self.__board[y][x] == 9:
self.ended = True
self.won = False
value = 9
self.__sfx.play_sound('trap-activated')
else:
self.__sfx.play_sound('slash')
self.view[y][x] = 1
# create method to check game status (won/lost)
self.__check_completeness()
self.update_board(x, y, True)
def __clear_empty_zone(self, x, y):
if x < 1 or self.__width + 1 <= x or y < 1 or self.__height + 1 <= y:
return
if self.view[y][x] != 0:
return
self.view[y][x] = 1
if self.__board[y][x] > 0:
return
if self.view[y-1][x-1] == 0:
self.__clear_empty_zone(x-1, y-1)
if self.view[y-1][x] == 0:
self.__clear_empty_zone(x, y-1)
if self.view[y-1][x+1] == 0:
self.__clear_empty_zone(x+1, y-1)
if self.view[y][x-1] == 0:
self.__clear_empty_zone(x-1, y)
if self.view[y][x+1] == 0:
self.__clear_empty_zone(x+1, y)
if self.view[y+1][x-1] == 0:
self.__clear_empty_zone(x-1, y+1)
if self.view[y+1][x] == 0:
self.__clear_empty_zone(x, y+1)
if self.view[y+1][x+1] == 0:
self.__clear_empty_zone(x+1, y+1)
def is_solved(self):
closed_count = 0
for y in range(1, self.__height + 1):
for x in range(1, self.__width + 1):
if self.view[y][x] != 1 or self.__board[y][x] == 9:
closed_count += 1
return closed_count == self.__traps_count
def __draw_init_board(self):
self.__gfx.preload_pictures()
self.__gfx.preload_sounds()
self.__gfx.draw_board(self.__width, self.__height, self.__scene)
self.__mode_display.show()
self.__restart_display.show()
self.__time_display.display()
def update_board(self, x, y, full=False):
if full:
value = 0
for y in range(1, self.__height + 1):
for x in range(1, self.__width + 1):
view = self.view[y][x]
value = self.__board[y][x]
self.__update_field(x, y, view, value)
else:
cell_value = -1
if self.view[y][x] == 2:
cell_value = 10
elif self.view[y][x] == 1:
cell_value = self.__board[y][x]
self.__gfx.update_tile(x, y, cell_value)
def __update_field(self, x, y, view, value):
cell_value = -1
if view == 2:
cell_value = 10
elif view == 1:
cell_value = value
self.__gfx.update_tile(x, y, cell_value)
def __check_completeness(self):
if not self.ended:
self.won = self.is_solved()
self.ended = self.won
if self.ended:
self.__time_display.stop_timer()
self.__time_display.display()
self.__restart_display.set_color(self.COLOR_FOCUS_RESTART)
if self.won:
self.__sfx.play_sound('congrats')
self.__info_display.set_color('#008800')
self.show_message(f'GG - You won! Press [R] to play again.')
else:
self.__sfx.play_sound('try-again')
self.__info_display.set_color('#FF0000')
self.show_message(f'Game is over! You lost. Press [R] to restart.')
def restart(self):
if self.__is_help_visible:
self.toggle_help()
self.__init_game()
# self.show_message('')
self.__sfx.play_sound('multi-clear')
self.update_board(0, 0, True)
self.__time_display.reset()
self.__started = False
self.__restart_display.set_color(self.COLOR_DEFAULT)
def toggle_help(self):
self.__is_help_visible = self.__gfx.toggle_help(self.__width, self.__height)
if not self.__is_help_visible:
self.update_board(-1, -1, True)
def __handle_mouse_click(self, pointerX, pointerY):
element, col, row = self.__converter.calc_click_result(pointerX, pointerY)
# debug
# self.show_message(f'{element} ({col}, {row})')
if element == 'tile':
if self.__is_help_visible:
return
if not self.__started:
self.__started = True
self.__time_display.start_timer()
self.open_field(col, row)
elif element == 'menu_m':
self.toggle_mode()
elif element == 'menu_r':
self.restart()
def bind_mouse(self):
žaidimas.pelęPaspaudus(self.__handle_mouse_click)
# pradžia
FIELD_WIDTH = 9
FIELD_HEIGHT = 9
TOTAL_TRAPS_COUNT = 10
splayer = SoundPlayer()
splayer.preload_sounds()
def test_sound_player_tick():
splayer.play_sound('dig')
def test_sound_player_trap():
splayer.play_sound('multi-clear')
def play_game():
# initiates game, draws board
game = DeTrapperGame(FIELD_WIDTH, FIELD_HEIGHT, TOTAL_TRAPS_COUNT)
# žaidimas.pelęAtleidusDešinįKlavišą(toggle_flag)
game.bind_mouse()
# klaviatūra: mode, restart
angis.atleidus('m', game.toggle_mode)
angis.atleidus('M', game.toggle_mode)
angis.atleidus('r', game.restart)
angis.atleidus('R', game.restart)
angis.atleidus('s', game.toggle_sound)
angis.atleidus('S', game.toggle_sound)
angis.atleidus('h', game.toggle_help)
angis.atleidus('H', game.toggle_help)
play_game()
Svarbu! Nepamiršk įsikelti žaidime naudojamus paveiksliukus ir garsus per failų tvarkyklę. Visi DeTrapper žaidimui reikalingi failai yra čia.