Skip to content

Commit d8bce7b

Browse files
rybarczykjsebastinas
authored andcommitted
add tron game to examples
1 parent 2b0522f commit d8bce7b

1 file changed

Lines changed: 233 additions & 0 deletions

File tree

examples/tron.py

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
import time
2+
import sys
3+
4+
from curtsies import FullscreenWindow, Input, FSArray, fmtstr, fsarray
5+
from curtsies.fmtfuncs import (
6+
bold,
7+
yellow,
8+
on_blue,
9+
cyan,
10+
on_yellow,
11+
on_red,
12+
black,
13+
)
14+
import curtsies.events
15+
16+
17+
class Cycle:
18+
def __init__(self, attr_dict):
19+
self.appearance = attr_dict["appearance"]
20+
self.x, self.y = attr_dict["x"], attr_dict["y"]
21+
self.keylist = attr_dict["keys"]
22+
self.dir = 0
23+
24+
def move(self, grid):
25+
d = self.dir
26+
if d == 0:
27+
self.x += 1
28+
elif d == 90:
29+
self.y -= 1
30+
elif d == 180:
31+
self.x -= 1
32+
elif d == 270:
33+
self.y += 1
34+
35+
def face(self, newdir):
36+
""" turn to face the given direction """
37+
if not (newdir % 180 == self.dir % 180):
38+
self.dir = newdir
39+
40+
def paint(self, grid):
41+
""" given a grid object, adds self to the grid and returns new grid
42+
"""
43+
grid[self.y, self.x] = self.appearance
44+
return grid
45+
46+
47+
class Bot(Cycle):
48+
""" Dumb bot that only turns right when it's about to crash
49+
"""
50+
51+
def move(self, grid):
52+
53+
a = self.getNextSquareCoords()
54+
if grid[a[0], a[1]] != [" "]:
55+
self.turnLeft()
56+
super().move(grid)
57+
58+
def turnRight(self):
59+
self.dir = (self.dir - 90) % 360
60+
61+
def turnLeft(self):
62+
self.dir = (self.dir + 90) % 360
63+
64+
def getNextSquareCoords(self, n=2):
65+
""" Returns the x, y coords of the box n spots in front of the bot.
66+
"""
67+
if self.dir == 0:
68+
return (self.y, self.x + n)
69+
elif self.dir == 90:
70+
return (self.y - n, self.x)
71+
elif self.dir == 180:
72+
return (self.y, self.x - n)
73+
else:
74+
return (self.y + n, self.x)
75+
76+
77+
class gameboard:
78+
def __init__(self, width, height, players):
79+
self.width = width
80+
self.height = height
81+
self.grid = FSArray(height, width)
82+
83+
self.players = players
84+
self.numplayers = len(self.players)
85+
86+
def draw_border(self):
87+
""" draw outer border """
88+
box = on_yellow(black(bold("!")))
89+
for x in range(1, self.width):
90+
self.grid[1, x] = box
91+
self.grid[self.height - 1, x] = box
92+
for y in range(1, self.height):
93+
self.grid[y, 1] = box
94+
self.grid[y, self.width - 1] = box
95+
96+
def process_event(self, key):
97+
if key == " ":
98+
sys.exit()
99+
100+
for player in self.players:
101+
if key in player.keylist:
102+
player.face(player.keylist[key])
103+
return False
104+
105+
def tick(self):
106+
""" Do one frame of work. Returns the winner if there
107+
is a crash
108+
"""
109+
# list of players alive this frame
110+
temp_players = self.players[:]
111+
112+
for bike in self.players:
113+
bike.move(self.grid)
114+
115+
if self.grid[bike.y, bike.x] == [" "]:
116+
self.grid[bike.y, bike.x] = bike.appearance
117+
else: # crashed
118+
temp_players.remove(bike)
119+
self.grid[bike.y, bike.x] = fmtstr("X", "red", "bold", "on_black")
120+
121+
if len(temp_players) == 0:
122+
return "tie"
123+
elif len(temp_players) < len(self.players):
124+
return temp_players
125+
126+
def winner_msg(self, tick):
127+
if len(tick) != 1:
128+
msg = fmtstr("it's a tie!", "red")
129+
else:
130+
winner = tick[0]
131+
winner_name = winner.appearance
132+
msg = fmtstr("winner:", "yellow")
133+
msg += " " + (winner_name * 5)
134+
return msg
135+
136+
137+
class Frame(curtsies.events.ScheduledEvent):
138+
pass
139+
140+
141+
def do_introduction(window):
142+
h, w = window.height, window.width
143+
144+
messages = [
145+
"two player tron",
146+
fmtstr("player 1:", "on_blue", "cyan") + " wasd",
147+
fmtstr("player 2:", "on_red", "yellow") + " arrow keys",
148+
]
149+
150+
billboard = FSArray(h, w)
151+
msg_row = h // 2 - 2
152+
for msg in messages:
153+
billboard[
154+
msg_row, w // 2 - len(msg) // 2 : w // 2 + len(msg) // 2 + 1
155+
] = fsarray([msg])
156+
msg_row += 1
157+
window.render_to_terminal(billboard)
158+
159+
# countdown msg
160+
for i in range(3, 0, -1):
161+
billboard[msg_row, w // 2] = fmtstr(str(i), "red")
162+
window.render_to_terminal(billboard)
163+
time.sleep(1)
164+
165+
166+
def mainloop(window, p2_bot=False):
167+
p1_attrs = {
168+
"appearance": on_blue((cyan("1"))),
169+
"x": window.width // 4,
170+
"y": window.height // 2,
171+
"keys": {"w": 90, "a": 180, "s": 270, "d": 0},
172+
}
173+
174+
p2_attrs = {
175+
"appearance": on_red((yellow("2"))),
176+
"x": 3 * window.width // 4,
177+
"y": window.height // 2,
178+
"keys": {"<UP>": 90, "<LEFT>": 180, "<DOWN>": 270, "<RIGHT>": 0},
179+
}
180+
181+
FPS = 15
182+
183+
players = [Cycle(p1_attrs), Cycle(p2_attrs)]
184+
if p2_bot: # make p2 a bot
185+
players[1] = Bot(p2_attrs)
186+
187+
world = gameboard(window.width, window.height, players)
188+
dt = 1 / FPS
189+
world.draw_border()
190+
window.render_to_terminal(world.grid)
191+
192+
reactor = Input()
193+
schedule_next_frame = reactor.scheduled_event_trigger(Frame)
194+
schedule_next_frame(when=time.time())
195+
with reactor:
196+
for c in reactor:
197+
if isinstance(c, Frame):
198+
tick = world.tick()
199+
window.render_to_terminal(world.grid)
200+
if not tick: # if no crashes
201+
when = c.when + dt
202+
while when < time.time():
203+
when += dt
204+
schedule_next_frame(when)
205+
else: # if crashed
206+
world.grid[0:4, 0:25] = fsarray(
207+
[
208+
world.winner_msg(tick),
209+
"r to restart",
210+
"q to quit",
211+
"b to make player 2 a bot",
212+
]
213+
)
214+
window.render_to_terminal(world.grid)
215+
elif c.lower() in ["r", "q", "b"]:
216+
break
217+
else: # common case
218+
world.process_event(c)
219+
if c.lower() == "r":
220+
mainloop(window, p2_bot)
221+
elif c.lower() == "b":
222+
mainloop(window, True)
223+
224+
225+
def main():
226+
with FullscreenWindow(sys.stdout) as window:
227+
do_introduction(window)
228+
229+
mainloop(window)
230+
231+
232+
if __name__ == "__main__":
233+
main()

0 commit comments

Comments
 (0)