Cleaning
This commit is contained in:
parent
baf286a48e
commit
2dd72ff011
8
play.py
8
play.py
|
@ -5,9 +5,9 @@ with contextlib.redirect_stdout(None):
|
|||
import pygame
|
||||
|
||||
from snake.map import Map
|
||||
from snake.game import Game
|
||||
from snake.game import Game, PositionPlayer
|
||||
from snake.window import Window
|
||||
from snake.player import Player, Direction, ConstantDecision, KeyboardDecision
|
||||
from snake.player import Player, Direction, ConstantPlayer, KeyboardPlayer
|
||||
|
||||
def main():
|
||||
pygame.init()
|
||||
|
@ -16,8 +16,8 @@ def main():
|
|||
height = 40
|
||||
|
||||
game = Game(width, height, [
|
||||
Player(0, 0, (255, 0, 0), KeyboardDecision(Direction.RIGHT)),
|
||||
Player(width - 1, height - 1, (0, 255, 0), ConstantDecision(Direction.LEFT)),
|
||||
PositionPlayer(1, KeyboardPlayer(Direction.RIGHT), [0, 0]),
|
||||
PositionPlayer(2, ConstantPlayer(Direction.LEFT), [width - 1, height - 1]),
|
||||
])
|
||||
|
||||
window = Window(game, 20)
|
||||
|
|
104
snake/game.py
104
snake/game.py
|
@ -3,10 +3,54 @@ This module contains everything related to the game.
|
|||
"""
|
||||
|
||||
from time import sleep
|
||||
from enum import Enum
|
||||
import pygame
|
||||
|
||||
from snake.map import Map
|
||||
|
||||
class Case(Enum):
|
||||
EMPTY = 0
|
||||
WALL = -1
|
||||
PLAYER_ONE_BODY = 1
|
||||
PLAYER_ONE_HEAD = 2
|
||||
PLAYER_TWO_BODY = 3
|
||||
PLAYER_TWO_HEAD = 4
|
||||
|
||||
def color(self):
|
||||
if self == Case.EMPTY:
|
||||
return (255, 255, 255)
|
||||
elif self == Case.WALL:
|
||||
return (0, 0, 0)
|
||||
elif self == Case.PLAYER_ONE_BODY:
|
||||
return (128, 0, 0)
|
||||
elif self == Case.PLAYER_ONE_HEAD:
|
||||
return (255, 0, 0)
|
||||
elif self == Case.PLAYER_TWO_BODY:
|
||||
return (0, 128, 0)
|
||||
elif self == Case.PLAYER_TWO_HEAD:
|
||||
return (0, 255, 0)
|
||||
else:
|
||||
return None
|
||||
|
||||
class PositionPlayer:
|
||||
def __init__(self, id, player, position):
|
||||
self.id = id
|
||||
self.player = player
|
||||
self.position = position
|
||||
self.alive = True
|
||||
|
||||
def body(self):
|
||||
if self.id == 1:
|
||||
return Case.PLAYER_ONE_BODY
|
||||
elif self.id == 2:
|
||||
return Case.PLAYER_TWO_BODY
|
||||
|
||||
def head(self):
|
||||
if self.id == 1:
|
||||
return Case.PLAYER_ONE_HEAD
|
||||
elif self.id == 2:
|
||||
return Case.PLAYER_TWO_HEAD
|
||||
|
||||
class Game:
|
||||
"""
|
||||
This class contains the map of the game, and the players.
|
||||
|
@ -14,7 +58,7 @@ class Game:
|
|||
It allows to update the player depending on their strategies, and run the game.
|
||||
"""
|
||||
|
||||
def __init__(self, width, height, players = []):
|
||||
def __init__(self, width, height, pps):
|
||||
"""
|
||||
Returns a new game from its width, height, and number of players.
|
||||
|
||||
|
@ -22,11 +66,14 @@ class Game:
|
|||
"""
|
||||
self.width = width
|
||||
self.height = height
|
||||
self.map = Map(width, height)
|
||||
self.players = players
|
||||
self.history = [Map(width, height, Case.EMPTY, Case.WALL)]
|
||||
self.pps = pps
|
||||
|
||||
for player in self.players:
|
||||
self.map[player.position[1],player.position[0]] = player
|
||||
for pp in self.pps:
|
||||
self.map()[pp.position[0], pp.position[1]] = pp.head()
|
||||
|
||||
def map(self):
|
||||
return self.history[-1]
|
||||
|
||||
def next_frame(self, window = None):
|
||||
"""
|
||||
|
@ -38,39 +85,40 @@ class Game:
|
|||
that is already occupied or that is outside of the map, the players
|
||||
dies.
|
||||
"""
|
||||
# Clone map
|
||||
self.history.append(self.map().clone())
|
||||
|
||||
# Set previous heads to body
|
||||
for pp in self.pps:
|
||||
self.map()[pp.position[0], pp.position[1]] = pp.body()
|
||||
|
||||
# Manage the events
|
||||
if window:
|
||||
|
||||
while True:
|
||||
|
||||
event = pygame.event.poll()
|
||||
|
||||
if event.type == pygame.NOEVENT:
|
||||
break
|
||||
|
||||
for player in self.players:
|
||||
player.manage_event(event)
|
||||
for pp in self.pps:
|
||||
pp.player.manage_event(event)
|
||||
|
||||
for player in self.players:
|
||||
for pp in self.pps:
|
||||
|
||||
if not player.alive:
|
||||
continue
|
||||
|
||||
player.position = player.next_position(self.map)
|
||||
pp.position = pp.player.next_position(pp.position, self)
|
||||
|
||||
# Check boundaries
|
||||
if player.position[0] < 0 or player.position[1] < 0 or \
|
||||
player.position[0] >= self.map.width or player.position[1] >= self.map.height:
|
||||
if pp.position[0] < 0 or pp.position[1] < 0 or \
|
||||
pp.position[0] >= self.width or pp.position[1] >= self.height:
|
||||
|
||||
player.alive = False
|
||||
pp.alive = False
|
||||
|
||||
# Check collision
|
||||
elif self.map.data[player.position[0]][player.position[1]] is not None:
|
||||
# Player lose
|
||||
player.alive = False
|
||||
elif self.map()[pp.position[0], pp.position[1]] is not Case.EMPTY:
|
||||
pp.alive = False
|
||||
|
||||
else:
|
||||
self.map.data[player.position[0]][player.position[1]] = player
|
||||
self.map()[pp.position[0], pp.position[1]] = pp.head()
|
||||
|
||||
|
||||
def main_loop(self, window = None):
|
||||
|
@ -81,7 +129,7 @@ class Game:
|
|||
"""
|
||||
|
||||
if window:
|
||||
window.render_map(self.map)
|
||||
window.render_map(self.map())
|
||||
|
||||
while True:
|
||||
alive_count = 0
|
||||
|
@ -90,14 +138,14 @@ class Game:
|
|||
if window:
|
||||
sleep(0.1)
|
||||
|
||||
for player in self.players:
|
||||
if player.alive:
|
||||
player.direction = player.decision.action(self.map) or player.direction
|
||||
for pp in self.pps:
|
||||
if pp.alive:
|
||||
pp.player.direction = pp.player.action(self) or pp.direction
|
||||
|
||||
self.next_frame(window)
|
||||
|
||||
for (index, player) in enumerate(self.players):
|
||||
if player.alive:
|
||||
for (index, pp) in enumerate(self.pps):
|
||||
if pp.alive:
|
||||
alive_count += 1
|
||||
alive = index
|
||||
|
||||
|
@ -110,5 +158,5 @@ class Game:
|
|||
break
|
||||
|
||||
if window:
|
||||
window.render_map(self.map)
|
||||
window.render_map(self.map())
|
||||
|
||||
|
|
26
snake/map.py
26
snake/map.py
|
@ -2,28 +2,38 @@
|
|||
This module contains the Map class.
|
||||
"""
|
||||
|
||||
def is_on_border(i, j, w ,h):
|
||||
return i == 0 or i == w - 1 or j == 0 or j == h - 1
|
||||
|
||||
class Map:
|
||||
"""
|
||||
The map of the game.
|
||||
|
||||
It basically consists of a matrix with a certain width and height. You can
|
||||
access its items by using the [] operator. This matrix can contain players
|
||||
or None if the square is empty.
|
||||
access its items by using the [] operator.
|
||||
"""
|
||||
def __init__(self, width, height):
|
||||
def __init__(self, w, h, empty, wall):
|
||||
"""
|
||||
Creates a new map from its width and its height.
|
||||
|
||||
The matrix is initialized with Nones
|
||||
"""
|
||||
self.width = width
|
||||
self.height = height
|
||||
self.data = [[None for i in range(height)] for j in range(width)]
|
||||
self.width = w
|
||||
self.height = h
|
||||
self._data = [[wall if is_on_border(i, j, w + 2, h + 2) else empty for i in range(h + 2)] for j in range(w + 2)]
|
||||
|
||||
def clone(self):
|
||||
"""
|
||||
Creates a clone of the map.
|
||||
"""
|
||||
map = Map(self.width, self.height, 0, 0)
|
||||
map._data = [[ self._data[j][i] for i in range(self.height + 2)] for j in range(self.width + 2) ]
|
||||
return map
|
||||
|
||||
def __getitem__(self, index):
|
||||
(i, j) = index
|
||||
return self.data[j][i]
|
||||
return self._data[i+1][j+1]
|
||||
|
||||
def __setitem__(self, position, other):
|
||||
(i, j) = position
|
||||
self.data[j][i] = other
|
||||
self._data[i+1][j+1] = other
|
||||
|
|
|
@ -15,7 +15,7 @@ class Direction(Enum):
|
|||
DOWN = 3
|
||||
LEFT = 4
|
||||
|
||||
class Decision:
|
||||
class Player:
|
||||
"""
|
||||
This is the base class for decisions.
|
||||
|
||||
|
@ -25,7 +25,18 @@ class Decision:
|
|||
def __init__(self):
|
||||
pass
|
||||
|
||||
def action(self, game_map):
|
||||
def next_position(self, current_position, game):
|
||||
direction = self.action(game)
|
||||
if direction == Direction.UP:
|
||||
return (current_position[0] - 1, current_position[1])
|
||||
if direction == Direction.RIGHT:
|
||||
return (current_position[0], current_position[1] + 1)
|
||||
if direction == Direction.DOWN:
|
||||
return (current_position[0] + 1, current_position[1])
|
||||
if direction == Direction.LEFT:
|
||||
return (current_position[0], current_position[1] - 1)
|
||||
|
||||
def action(self, game):
|
||||
"""
|
||||
This function is called each time to ask the player his action.
|
||||
|
||||
|
@ -42,7 +53,7 @@ class Decision:
|
|||
"""
|
||||
pass
|
||||
|
||||
class KeyboardDecision(Decision):
|
||||
class KeyboardPlayer(Player):
|
||||
""""
|
||||
This is the key board interaction.
|
||||
|
||||
|
@ -52,7 +63,7 @@ class KeyboardDecision(Decision):
|
|||
"""
|
||||
Creates a keyboard decision with a default direction.
|
||||
"""
|
||||
super(KeyboardDecision, self).__init__()
|
||||
super(KeyboardPlayer, self).__init__()
|
||||
self.direction = initial_direction
|
||||
|
||||
def manage_event(self, event):
|
||||
|
@ -69,55 +80,19 @@ class KeyboardDecision(Decision):
|
|||
if event.key == pygame.K_s:
|
||||
self.direction = Direction.DOWN
|
||||
|
||||
def action(self, game_map):
|
||||
def action(self, game):
|
||||
"""
|
||||
Returns the direction of the snake.
|
||||
"""
|
||||
return self.direction
|
||||
|
||||
class ConstantDecision(Decision):
|
||||
class ConstantPlayer(Player):
|
||||
"""
|
||||
This is a class that always returns the same decision.
|
||||
"""
|
||||
def __init__(self, direction):
|
||||
super(ConstantDecision, self).__init__()
|
||||
super(ConstantPlayer, self).__init__()
|
||||
self.direction = direction
|
||||
|
||||
def action(self, game_map):
|
||||
def action(self, game):
|
||||
return self.direction
|
||||
|
||||
class Player:
|
||||
"""
|
||||
This class represents a snake, with its current position, its starting
|
||||
position, its direction, and the fact that its alive of not.
|
||||
"""
|
||||
def __init__(self, row, col, color, decision):
|
||||
"""
|
||||
Creates a new player from its initial position, its color and its
|
||||
decision algorithm.
|
||||
"""
|
||||
self.color = color
|
||||
self.position = (row, col)
|
||||
self.start = (row, col)
|
||||
self.decision = decision
|
||||
self.alive = True
|
||||
|
||||
def next_position(self, map):
|
||||
"""
|
||||
Updates the position of the snake depending on its decision algorithm.
|
||||
"""
|
||||
direction = self.decision.action(map)
|
||||
if direction == Direction.UP:
|
||||
return (self.position[0] - 1, self.position[1])
|
||||
if direction == Direction.RIGHT:
|
||||
return (self.position[0], self.position[1] + 1)
|
||||
if direction == Direction.DOWN:
|
||||
return (self.position[0] + 1, self.position[1])
|
||||
if direction == Direction.LEFT:
|
||||
return (self.position[0], self.position[1] - 1)
|
||||
|
||||
def manage_event(self, event):
|
||||
"""
|
||||
Updates the decision depending on the event.
|
||||
"""
|
||||
self.decision.manage_event(event)
|
||||
|
|
|
@ -19,7 +19,7 @@ class Window:
|
|||
(width, height) = (factor * (game.width + 2), factor * (game.height + 2))
|
||||
|
||||
self.screen = pygame.display.set_mode((width, height))
|
||||
self.render_map(game.map)
|
||||
self.render_map(game.map())
|
||||
|
||||
def scale_box(self, row, col, width, height):
|
||||
"""
|
||||
|
@ -44,7 +44,7 @@ class Window:
|
|||
if block:
|
||||
pygame.draw.rect(
|
||||
self.screen,
|
||||
block.color,
|
||||
self.scale_box(row+1.1, col+1.1, 0.9, 0.9))
|
||||
block.color(),
|
||||
self.scale_box(col+1.1, row+1.1, 0.9, 0.9))
|
||||
|
||||
pygame.display.flip()
|
||||
|
|
Loading…
Reference in New Issue