Skip to content

Instantly share code, notes, and snippets.

@ph1ee
Created January 18, 2022 12:21
Show Gist options
  • Save ph1ee/e6214105fc8a2e48b46b5d4c486aa101 to your computer and use it in GitHub Desktop.
Save ph1ee/e6214105fc8a2e48b46b5d4c486aa101 to your computer and use it in GitHub Desktop.
a territory game for the yearly lucky draw
#!/usr/bin/env python3
import pdb
import copy
from tkinter import *
from tkinter import Tk, Canvas, Frame, BOTH, LEFT, RIGHT
from functools import cmp_to_key
from collections import namedtuple
import random
from random import choice, shuffle, sample
Cell = namedtuple("Cell", ["x", "y", "player"])
Player = namedtuple("Player", ["name", "color", "pos", "occupied", "entries"])
TOTAL_MONEY = 72000 - 7200
bonus_setup = [18000, 12000, 8000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 000, 1000]
# assert sum(bonus_setup) == TOTAL_MONEY
class Model:
def __init__(self, pi):
colors = [
"Salmon",
"Pink",
"Orange",
"Khaki",
"Violet",
"SeaGreen",
"Olive",
"Aqua",
"SkyBlue",
"Bisque",
"Sienna",
"Azure",
]
allplayer = []
occupied = []
shuffle(pi)
for nam, pos in pi:
clr = choice(colors)
colors.remove(clr)
if not pos: # no preset position
pos = (choice(range(len(pi))), choice(range(len(pi))))
while pos in occupied:
pos = (choice(range(len(pi))), choice(range(len(pi))))
allplayer.append(Player(nam, clr, pos, [], []))
occupied.append(pos)
board = []
for y in range(len(allplayer)):
board.append([Cell(x, y, []) for x in range(len(allplayer))])
self.ui = None
self.board = board
self.round = 0
self.allplayer = allplayer
self.players = copy.copy(allplayer)
def setup(self):
for p in self.allplayer:
y, x = p.pos
c = self.board[y][x]
c.player.append(p)
p.occupied.append(c)
for u, v in [
(c.y - 1, c.x),
(c.y + 1, c.x),
(c.y, c.x - 1),
(c.y, c.x + 1),
]:
if u < 0 or u == len(self.board) or v < 0 or v == len(self.board):
continue
c = self.board[u][v]
if c not in p.entries and not c.player:
p.entries.append(c)
if self.ui:
self.ui.update()
def step(self):
if not self.players:
return
stopped = []
for p in sample(self.players, len(self.players)):
rmlist = []
for c in p.entries:
if c.player:
rmlist.append(c)
for c in rmlist:
p.entries.remove(c)
if not p.entries:
stopped.append(p)
print("{} GG".format(p.name))
continue
c = choice(p.entries)
c.player.append(p)
p.occupied.append(c)
p.entries.remove(c)
assert c.player[0].name == p.name
for u, v in [
(c.y - 1, c.x),
(c.y + 1, c.x),
(c.y, c.x - 1),
(c.y, c.x + 1),
]:
if u < 0 or u == len(self.board) or v < 0 or v == len(self.board):
continue
c = self.board[u][v]
if c not in p.entries and not c.player:
p.entries.append(c)
for p in stopped:
self.players.remove(p)
if self.ui:
self.ui.animate(1000 + 100 * self.round)
self.round += 1
def set_ui(self, ui):
self.ui = ui
class Gui(Frame):
def __init__(self, model):
super().__init__()
self.model = model
self.model.set_ui(self)
self.initUI()
def compute_bonus(self, ranking):
countlist = []
count = 0
last_occupied_number = 0
for i, p in enumerate(ranking):
if last_occupied_number == len(p.occupied):
count += 1
else:
if count > 0:
countlist.append(count)
count = 1
last_occupied_number = len(p.occupied)
if count > 0:
countlist.append(count)
bonus = []
remaining = list(reversed(bonus_setup))
for c in countlist:
accum = 0
for i in range(c):
accum += remaining.pop()
avg = accum / c
for i in range(c):
bonus.append(avg)
assert not remaining # should use up all money
assert len(bonus) == len(self.model.allplayer)
# assert int(sum(bonus)) == TOTAL_MONEY
return bonus
def update(self):
self.board_canvas.delete("all")
padding = 16
size = 30
width = 2
for y, row in enumerate(self.model.board):
for x, c in enumerate(row):
x0 = padding + size * c.x
y0 = padding + size * c.y
self.board_canvas.create_rectangle(
x0,
y0,
x0 + size,
y0 + size,
outline="#222",
fill=c.player[0].color if c.player else "gray",
width=width,
)
if c.player:
u, v = c.player[0].pos
self.board_canvas.create_text(
x0 + size / 2,
y0 + size / 2,
text=c.player[0].name[:2].upper(),
font=(
"Sans",
10,
"normal {}".format("bold" if u == y and v == x else ""),
),
)
self.rank_canvas.delete("all")
ranking = sorted(
self.model.allplayer,
key=cmp_to_key(lambda a, b: len(b.occupied) - len(a.occupied)),
)
bonus = self.compute_bonus(ranking)
for i, p in enumerate(ranking):
x0 = padding
y0 = padding + size * i
self.rank_canvas.create_text(
x0 + size / 2, y0 + size / 2, text=str(len(p.occupied))
)
x0 += size
self.rank_canvas.create_rectangle(
x0, y0, x0 + size, y0 + size, outline="#222", fill=p.color, width=width
)
self.rank_canvas.create_text(
x0 + size / 2, y0 + size / 2, text=p.name[:2].upper()
)
x0 += size * 3
self.rank_canvas.create_text(
x0, y0 + size / 2, text="NT ${:,.1f}".format(450 * len(p.occupied))
)
def animate(self, speed):
self.update()
self.after(speed, self.model.step)
def initUI(self):
self.master.title("Territory")
main_frame = Frame(self, relief=RAISED, borderwidth=1)
main_frame.pack(fill=BOTH, expand=True)
self.board_canvas = Canvas(main_frame)
self.board_canvas.pack(side=LEFT, fill=BOTH, expand=1)
self.rank_canvas = Canvas(main_frame)
self.rank_canvas.pack(side=LEFT, fill=BOTH, expand=1)
self.pack(fill=BOTH, expand=1)
start_button = Button(self, text="Start", command=self.model.step)
start_button.pack(side=RIGHT)
def main():
random.seed()
pi0 = [
("Anderson", (11, 11)),
("Colin", None),
("David", None),
("Dio", None),
("Dylan", None),
("Justin", None),
("Keeli", None),
("Mark", None),
("Paul", None),
("Sam", (0, 0)),
("Scott", None),
("Steve", None),
]
assert len(pi0) == len(bonus_setup)
pi1 = [
("Anderson", (0, 0)),
("Colin", (1, 0)),
("David", (2, 0)),
("Dio", (3, 0)),
("Dylan", (4, 0)),
("Justin", (5, 0)),
("Keeli", (6, 0)),
("Mark", (7, 0)),
("Paul", (8, 0)),
("Sam", (9, 0)),
("Scott", (10, 0)),
("Steve", (11, 0)),
]
assert len(pi1) == len(bonus_setup)
pi2 = [
("Anderson", (0, 0)),
("Colin", (1, 1)),
("David", (2, 2)),
("Dio", (3, 3)),
("Dylan", (4, 4)),
("Justin", (5, 5)),
("Keeli", (6, 6)),
("Mark", (7, 7)),
("Paul", (8, 8)),
("Sam", (9, 9)),
("Scott", (10, 10)),
("Steve", (11, 11)),
]
assert len(pi2) == len(bonus_setup)
root = Tk()
root.geometry("600x430")
model = Model(pi0)
game = Gui(model)
model.setup()
root.mainloop()
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment