Skip to content

Instantly share code, notes, and snippets.

@leonmak
Last active February 15, 2025 12:51
Show Gist options
  • Save leonmak/f38fb76a4f3787d548fad0f14221e211 to your computer and use it in GitHub Desktop.
Save leonmak/f38fb76a4f3787d548fad0f14221e211 to your computer and use it in GitHub Desktop.
multi-threaded board game
import random
import threading
import time
num_p = 10
m, n = 5, 5
# position (y,x) - lock, players in pos
board_locks = {(i//m, i % n): threading.Lock() for i in range(m*n)}
board_players = {(i//m, i % n): set() for i in range(m*n)}
class Player(threading.Thread):
def __init__(self, id, turns=10):
super().__init__()
self.id = id
self.turns = turns
self.pos_lock = threading.Lock() # locks all attr
self.pos = (random.randint(0, m-1), random.randint(0, n-1))
self.strength = 0
self.alive = True # can be set by other player
def run(self):
for _ in range(self.turns):
time.sleep(random.randint(0, 50)/10) # sleep 0-5s
if not self.alive: # other init and kiled you
return
other_ps = self.move()
for p in other_ps:
you_died = self.battle(p) # you init
if you_died:
self.alive = False
return
def battle(self, other):
# check other is alive
if not other.alive:
return
(lock1, lock2) = (self.pos_lock, other.pos_lock) if self.id < other.id else (
other.pos_lock, self.pos_lock)
# prevent deadlock if both acquire their own pos_lock, then try acquire the other
with lock1:
with lock2: # prevent player from moving (and fighting other)
print(f"===\nP{self.id} fights P{other.id} at {self.pos}")
if self.strength < other.strength:
self.remove_pos()
self.alive = False
print(f"P{self.id} has died\n")
return True # you died
elif self.strength > other.strength:
other.remove_pos()
other.alive = False
print(f"P{other.id} has died")
print(f"P{self.id} gained 4 str\n")
self.strength += 4
return False
return False # did not die
def remove_pos(self): # from other / self
with board_locks[self.pos]:
board_players[self.pos].discard(self.id)
def move(self): # locked self pos
# get lock to remove prev
with board_locks[self.pos]:
board_players[self.pos].discard(self.id)
# increase str when move
# set new pos, return players there
new_pos = (random.randint(0, m-1), random.randint(0, n-1))
dist = abs(new_pos[0]-self.pos[0]) + abs(new_pos[1]-self.pos[1])
self.strength += dist
if dist > 0:
print(f"P{self.id} gained {dist} str to {self.strength}")
# acq lock to set new pos
with board_locks[new_pos]:
if not self.alive: # someone killed you before you moved
return []
self.pos = new_pos
other_ps = [players[i] for i in board_players[self.pos]]
board_players[self.pos].add(self.id)
return other_ps
players = {i: Player(i) for i in range(num_p)}
for p in players.values():
p.start() # run
for p in players.values():
p.join()
for p in players.values():
print(f'P{p.id} (str: {p.strength}) is {"alive" if p.alive else "dead"}')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment