maxman: add initial implementation
This commit is contained in:
12
Pipfile
Normal file
12
Pipfile
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
[[source]]
|
||||||
|
url = "https://pypi.org/simple"
|
||||||
|
verify_ssl = true
|
||||||
|
name = "pypi"
|
||||||
|
|
||||||
|
[packages]
|
||||||
|
pygame = "*"
|
||||||
|
|
||||||
|
[dev-packages]
|
||||||
|
|
||||||
|
[requires]
|
||||||
|
python_version = "3.12"
|
84
Pipfile.lock
generated
Normal file
84
Pipfile.lock
generated
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
{
|
||||||
|
"_meta": {
|
||||||
|
"hash": {
|
||||||
|
"sha256": "88a6aed012a38fd06a01fb57f063f0e338b4d184b7c5c974a94737eb695ba85a"
|
||||||
|
},
|
||||||
|
"pipfile-spec": 6,
|
||||||
|
"requires": {
|
||||||
|
"python_version": "3.12"
|
||||||
|
},
|
||||||
|
"sources": [
|
||||||
|
{
|
||||||
|
"name": "pypi",
|
||||||
|
"url": "https://pypi.org/simple",
|
||||||
|
"verify_ssl": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"default": {
|
||||||
|
"pygame": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:03879ec299c9f4ba23901b2649a96b2143f0a5d787f0b6c39469989e2320caf1",
|
||||||
|
"sha256:074aa6c6e110c925f7f27f00c7733c6303407edc61d738882985091d1eb2ef17",
|
||||||
|
"sha256:0e24d05184e4195fe5ebcdce8b18ecb086f00182b9ae460a86682d312ce8d31f",
|
||||||
|
"sha256:1822d534bb7fe756804647b6da2c9ea5d7a62d8796b2e15d172d3be085de28c6",
|
||||||
|
"sha256:1a2a43802bb5e89ce2b3b775744e78db4f9a201bf8d059b946c61722840ceea8",
|
||||||
|
"sha256:1c289f2613c44fe70a1e40769de4a49c5ab5a29b9376f1692bb1a15c9c1c9bfa",
|
||||||
|
"sha256:1f3849f97372a3381c66955f99a0d58485ccd513c3d00c030b869094ce6997a6",
|
||||||
|
"sha256:224c308856334bc792f696e9278e50d099a87c116f7fc314cd6aa3ff99d21592",
|
||||||
|
"sha256:263b4a7cbfc9fe2055abc21b0251cc17dea6dff750f0e1c598919ff350cdbffe",
|
||||||
|
"sha256:2b34c73cb328024f8db3cb6487a37e54000148988275d8d6e5adf99d9323c937",
|
||||||
|
"sha256:30a8d7cf12363b4140bf2f93b5eec4028376ca1d0fe4b550588f836279485308",
|
||||||
|
"sha256:31648d38ecdc2335ffc0e38fb18a84b3339730521505dac68514f83a1092e3f4",
|
||||||
|
"sha256:34646ca20e163dc6f6cf8170f1e12a2e41726780112594ac061fa448cf7ccd75",
|
||||||
|
"sha256:35632035fd81261f2d797fa810ea8c46111bd78ceb6089d52b61ed7dc3c5d05f",
|
||||||
|
"sha256:35cf093a51cb294ede56c29d4acf41538c00f297fcf78a9b186fb7d23c0577b6",
|
||||||
|
"sha256:39690e9be9baf58b7359d1f3b2336e1fd6f92fedbbce42987be5df27f8d30718",
|
||||||
|
"sha256:3b3e619e33d11c297d7a57a82db40681f9c2c3ae1d5bf06003520b4fe30c435d",
|
||||||
|
"sha256:3b8a6e351665ed26ea791f0e1fd649d3f483e8681892caef9d471f488f9ea5ee",
|
||||||
|
"sha256:41f8779f52e0f6e6e6ccb8f0b5536e432bf386ee29c721a1c22cada7767b0cef",
|
||||||
|
"sha256:47a8415d2bd60e6909823b5643a1d4ef5cc29417d817f2a214b255f6fa3a1e4c",
|
||||||
|
"sha256:485239c7d32265fd35b76ae8f64f34b0637ae11e69d76de15710c4b9edcc7c8d",
|
||||||
|
"sha256:4f1559e7efe4efb9dc19d2d811d702f325d9605f9f6f9ececa39ee6890c798f5",
|
||||||
|
"sha256:4ff21201df6278b8ca2e948fb148ffe88f5481fd03760f381dd61e45954c7dff",
|
||||||
|
"sha256:5697528266b4716d9cdd44a5a1d210f4d86ef801d0f64ca5da5d0816704009d9",
|
||||||
|
"sha256:677e37bc0ea7afd89dde5a88ced4458aa8656159c70a576eea68b5622ee1997b",
|
||||||
|
"sha256:68c4e8e60b725ffc7a6c6ecd9bb5fcc5ed2d6e0e2a2c4a29a8454856ef16ad63",
|
||||||
|
"sha256:6cf2257447ce7f2d6de37e5fb019d2bbe32ed05a5721ace8bc78c2d9beaf3aee",
|
||||||
|
"sha256:6d58c8cf937815d3b7cdc0fa9590c5129cb2c9658b72d00e8a4568dea2ff1d42",
|
||||||
|
"sha256:6fe323acbf53a0195c8c98b1b941eba7ac24e3e2b28ae48e8cda566f15fc4945",
|
||||||
|
"sha256:74e1d6284100e294f445832e6f6343be4fe4748decc4f8a51131ae197dae8584",
|
||||||
|
"sha256:78fcd7643358b886a44127ff7dec9041c056c212b3a98977674f83f99e9b12d3",
|
||||||
|
"sha256:7d0a2794649defa57ef50b096a99f7113d3d0c2e32d1426cafa7d618eadce4c7",
|
||||||
|
"sha256:88d1cdacc2d3471eceab98bf0c93c14d3a8461f93e58e3d926f20d4de3a75554",
|
||||||
|
"sha256:9b30bc1220c457169571aac998e54b013aaeb732d2fd8744966cb1cfab1f61d1",
|
||||||
|
"sha256:9bd738fd4ecc224769d0b4a719f96900a86578e26e0105193658a32966df2aae",
|
||||||
|
"sha256:9dcff6cbba1584cf7732ce1dbdd044406cd4f6e296d13bcb7fba963fb4aeefc9",
|
||||||
|
"sha256:a0769eb628c818761755eb0a0ca8216b95270ea8cbcbc82227e39ac9644643da",
|
||||||
|
"sha256:a0bd67426c02ffe6c9827fc4bcbda9442fbc451d29b17c83a3c088c56fef2c90",
|
||||||
|
"sha256:bc12e4dea3e88ea8a553de6d56a37b704dbe2aed95105889f6afeb4b96e62097",
|
||||||
|
"sha256:c13edebc43c240fb0532969e914f0ccefff5ae7e50b0b788d08ad2c15ef793e4",
|
||||||
|
"sha256:c1b89eb5d539e7ac5cf75513125fb5f2f0a2d918b1fd6e981f23bf0ac1b1c24a",
|
||||||
|
"sha256:ce4b6c0bfe44d00bb0998a6517bd0cf9455f642f30f91bc671ad41c05bf6f6ae",
|
||||||
|
"sha256:cf2191b756ceb0e8458a761d0c665b0c70b538570449e0d39b75a5ba94ac5cf0",
|
||||||
|
"sha256:d29a84b2e02814b9ba925357fd2e1df78efe5e1aa64dc3051eaed95d2b96eafd",
|
||||||
|
"sha256:d75cbbfaba2b81434d62631d0b08b85fab16cf4a36e40b80298d3868927e1299",
|
||||||
|
"sha256:d78485c4d21133d6b2fbb504cd544ca655e50b6eb551d2995b3aa6035928adda",
|
||||||
|
"sha256:d851247239548aa357c4a6840fb67adc2d570ce7cb56988d036a723d26b48bff",
|
||||||
|
"sha256:daca456d5b9f52e088e06a127dec182b3638a775684fb2260f25d664351cf1ae",
|
||||||
|
"sha256:dc346965847aef00013fa2364f41a64f068cd096dcc7778fc306ca3735f0eedf",
|
||||||
|
"sha256:dd2d2650faf54f9a0f5bd0db8409f79609319725f8f08af6507a0609deadcad4",
|
||||||
|
"sha256:e58e2b0c791041e4bccafa5bd7650623ba1592b8fe62ae0a276b7d0ecb314b6c",
|
||||||
|
"sha256:e708fc8f709a0fe1d1876489345f2e443d47f3976d33455e2e1e937f972f8677",
|
||||||
|
"sha256:ed9a3d98adafa0805ccbaaff5d2996a2b5795381285d8437a4a5d248dbd12b4a",
|
||||||
|
"sha256:edda1f7cff4806a4fa39e0e8ccd75f38d1d340fa5fc52d8582ade87aca247d92",
|
||||||
|
"sha256:f02c1c7505af18d426d355ac9872bd5c916b27f7b0fe224749930662bea47a50",
|
||||||
|
"sha256:f30d1618672a55e8c6669281ba264464b3ab563158e40d89e8c8b3faa0febebd",
|
||||||
|
"sha256:fe0228501ec616779a0b9c4299e837877783e18df294dd690b9ab0eed3d8aaab"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==2.5.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"develop": {}
|
||||||
|
}
|
237
maxman.py
Normal file
237
maxman.py
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
import abc
|
||||||
|
import dataclasses
|
||||||
|
import math
|
||||||
|
import random
|
||||||
|
import sys
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
import pygame
|
||||||
|
|
||||||
|
|
||||||
|
# TODO replace with pygame.Rect
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class Entity:
|
||||||
|
pos: pygame.Vector2
|
||||||
|
size: int
|
||||||
|
|
||||||
|
@property
|
||||||
|
def top(self) -> int:
|
||||||
|
return self.pos.y
|
||||||
|
|
||||||
|
@property
|
||||||
|
def bottom(self) -> int:
|
||||||
|
return self.pos.y + self.size
|
||||||
|
|
||||||
|
@property
|
||||||
|
def left(self) -> int:
|
||||||
|
return self.pos.x
|
||||||
|
|
||||||
|
@property
|
||||||
|
def right(self) -> int:
|
||||||
|
return self.pos.x + self.size
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def draw(self, screen: pygame.Surface) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class Food(Entity):
|
||||||
|
color: str = "white"
|
||||||
|
growth: int = 5
|
||||||
|
size: int = 10
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def new(cls):
|
||||||
|
return cls(
|
||||||
|
pos=pygame.Vector2(random.randint(0, sw), random.randint(0, sh)),
|
||||||
|
)
|
||||||
|
|
||||||
|
def draw(self, screen: pygame.Surface) -> None:
|
||||||
|
p = pygame.Vector2(self.pos.x + self.size / 2, self.pos.y + self.size / 2)
|
||||||
|
pygame.draw.circle(screen, self.color, p, self.size / 2)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class Man(Entity):
|
||||||
|
name: str = None
|
||||||
|
speed: float = 500
|
||||||
|
color: str = "yellow"
|
||||||
|
alive: bool = True
|
||||||
|
size: int = 40
|
||||||
|
enabled: bool = True
|
||||||
|
|
||||||
|
key_up: int = pygame.K_w
|
||||||
|
key_down: int = pygame.K_s
|
||||||
|
key_left: int = pygame.K_a
|
||||||
|
key_right: int = pygame.K_d
|
||||||
|
|
||||||
|
def handle_input(self, inputs: pygame.key.ScancodeWrapper, dt: float) -> None:
|
||||||
|
if self.enabled:
|
||||||
|
horz, vert = 0, 0
|
||||||
|
if keys[self.key_up]:
|
||||||
|
vert = -1
|
||||||
|
if keys[self.key_down]:
|
||||||
|
vert = 1
|
||||||
|
if keys[self.key_left]:
|
||||||
|
horz = -1
|
||||||
|
if keys[self.key_right]:
|
||||||
|
horz = 1
|
||||||
|
self.walk(horz, vert)
|
||||||
|
|
||||||
|
def walk(self, horizontal, vertical):
|
||||||
|
horizontal = horizontal / abs(horizontal) if horizontal else 0
|
||||||
|
vertical = vertical / abs(vertical) if vertical else 0
|
||||||
|
mov = self.speed * dt
|
||||||
|
if horizontal != 0 and vertical != 0:
|
||||||
|
mov = math.sqrt(2 * mov**2) / 2
|
||||||
|
|
||||||
|
self.move(mov * horizontal, mov * vertical)
|
||||||
|
|
||||||
|
def move(self, dx: int, dy: int) -> None:
|
||||||
|
self.pos.x += dx
|
||||||
|
self.pos.y += dy
|
||||||
|
|
||||||
|
def draw(self, screen: pygame.Surface) -> None:
|
||||||
|
p = pygame.Vector2(self.pos.x + self.size / 2, self.pos.y + self.size / 2)
|
||||||
|
pygame.draw.circle(screen, self.color, p, self.size / 2)
|
||||||
|
|
||||||
|
def can_eat(self, other: Entity) -> bool:
|
||||||
|
overlaps_x = self.left <= other.left and self.right >= other.right
|
||||||
|
overlaps_y = self.top <= other.top and self.bottom >= other.bottom
|
||||||
|
return overlaps_x and overlaps_y and self.size >= other.size
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class AI:
|
||||||
|
man: Man = None
|
||||||
|
enemies: List[Man] = None
|
||||||
|
target = None
|
||||||
|
|
||||||
|
def decide(self):
|
||||||
|
if self.man is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
target = self.target
|
||||||
|
if random.random() * 100 < 10:
|
||||||
|
target = None
|
||||||
|
|
||||||
|
if target is None:
|
||||||
|
if targets := [e for e in self.enemies or [] if e.size < self.man.size]:
|
||||||
|
target = random.choice(targets)
|
||||||
|
|
||||||
|
if target is None:
|
||||||
|
target = food
|
||||||
|
|
||||||
|
self.target = target
|
||||||
|
|
||||||
|
horz = (target.pos.x + target.size / 2) - (self.man.pos.x + self.man.size / 2)
|
||||||
|
vert = (target.pos.y + target.size / 2) - (self.man.pos.y + self.man.size / 2)
|
||||||
|
self.man.walk(horz, vert)
|
||||||
|
|
||||||
|
def hook(self, player):
|
||||||
|
player.enabled = False
|
||||||
|
self.man = player
|
||||||
|
self.target = None
|
||||||
|
self.enemies = [p for p in players if p != player]
|
||||||
|
|
||||||
|
def release(self):
|
||||||
|
self.man.enabled = True
|
||||||
|
self.man = None
|
||||||
|
self.target = None
|
||||||
|
self.enemies = players
|
||||||
|
|
||||||
|
|
||||||
|
# set up pygame
|
||||||
|
pygame.init()
|
||||||
|
pygame.display.set_caption("Maxman")
|
||||||
|
screen = pygame.display.set_mode([1000, 1000])
|
||||||
|
clock = pygame.time.Clock()
|
||||||
|
running = True
|
||||||
|
paused = True
|
||||||
|
font = pygame.font.Font("freesansbold.ttf", 32)
|
||||||
|
space_was_pressed = False
|
||||||
|
dt = 0
|
||||||
|
|
||||||
|
# set up board
|
||||||
|
sw, sh = screen.get_width(), screen.get_height()
|
||||||
|
ai = AI()
|
||||||
|
maxman = Man(pygame.Vector2(sw / 4, sh / 4), name="Maxman")
|
||||||
|
blackman = Man(
|
||||||
|
pygame.Vector2(sw * 0.75, sh * 0.75),
|
||||||
|
name="Blackman",
|
||||||
|
color="black",
|
||||||
|
key_up=pygame.K_i,
|
||||||
|
key_down=pygame.K_k,
|
||||||
|
key_left=pygame.K_j,
|
||||||
|
key_right=pygame.K_l,
|
||||||
|
)
|
||||||
|
players = [maxman, blackman]
|
||||||
|
food = Food.new()
|
||||||
|
|
||||||
|
if any([arg in ["--ai", "-a"] for arg in sys.argv]):
|
||||||
|
ai.hook(blackman)
|
||||||
|
|
||||||
|
while running:
|
||||||
|
# check recent events
|
||||||
|
for event in pygame.event.get():
|
||||||
|
if event.type == pygame.QUIT:
|
||||||
|
running = False
|
||||||
|
|
||||||
|
# draw players in order of size
|
||||||
|
screen.fill("purple")
|
||||||
|
food.draw(screen)
|
||||||
|
for p in sorted(players, key=lambda p: p.size):
|
||||||
|
p.draw(screen)
|
||||||
|
|
||||||
|
keys = pygame.key.get_pressed()
|
||||||
|
if len(players) == 1:
|
||||||
|
text = font.render(
|
||||||
|
f"{players[0].name} wins",
|
||||||
|
True,
|
||||||
|
"black",
|
||||||
|
)
|
||||||
|
text_rect = text.get_rect()
|
||||||
|
screen.blit(
|
||||||
|
text,
|
||||||
|
pygame.Vector2((sw - text_rect.width) / 2, (sh - text_rect.height) / 2),
|
||||||
|
)
|
||||||
|
|
||||||
|
if keys[pygame.K_SPACE]:
|
||||||
|
running = False
|
||||||
|
|
||||||
|
else:
|
||||||
|
for p in players:
|
||||||
|
for op in players:
|
||||||
|
if p != op and p.can_eat(op):
|
||||||
|
players.remove(op)
|
||||||
|
|
||||||
|
if p.can_eat(food):
|
||||||
|
p.size += food.growth
|
||||||
|
food = food.new()
|
||||||
|
|
||||||
|
# handle inputs
|
||||||
|
if paused:
|
||||||
|
text = font.render("Pause", True, "black")
|
||||||
|
text_rect = text.get_rect()
|
||||||
|
screen.blit(
|
||||||
|
text,
|
||||||
|
pygame.Vector2((sw - text_rect.width) / 2, (sh - text_rect.height) / 2),
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
ai.decide()
|
||||||
|
for p in players:
|
||||||
|
p.handle_input(keys, dt)
|
||||||
|
|
||||||
|
if keys[pygame.K_SPACE]:
|
||||||
|
if not space_was_pressed:
|
||||||
|
paused = not paused
|
||||||
|
space_was_pressed = True
|
||||||
|
else:
|
||||||
|
space_was_pressed = False
|
||||||
|
|
||||||
|
pygame.display.flip()
|
||||||
|
dt = clock.tick(100) / 1000
|
||||||
|
|
||||||
|
pygame.quit()
|
Reference in New Issue
Block a user