~ubuntu-branches/ubuntu/wily/phabricator/wily

« back to all changes in this revision

Viewing changes to scripts/breakout.py

  • Committer: Package Import Robot
  • Author(s): Richard Sellam
  • Date: 2014-11-01 23:20:06 UTC
  • mto: This revision was merged to the branch mainline in revision 4.
  • Revision ID: package-import@ubuntu.com-20141101232006-mvlnp0cil67tsboe
Tags: upstream-0~git20141101/arcanist
Import upstream version 0~git20141101, component arcanist

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/env python
 
2
import sys
 
3
import time
 
4
import select
 
5
import curses
 
6
import curses.wrapper
 
7
 
 
8
entities = []
 
9
grid = []
 
10
 
 
11
class Wall:
 
12
    def collide(self, ball):
 
13
        return False
 
14
 
 
15
class Block:
 
16
    killed = 0
 
17
    total = 0
 
18
 
 
19
    def __init__(self, x, y, w, h, c):
 
20
        self.x = x
 
21
        self.y = y
 
22
        self.w = w
 
23
        self.h = h
 
24
        self.fmt = curses.A_BOLD | curses.color_pair(c)
 
25
        self.alive = True
 
26
        for i in range(self.x, self.x + self.w):
 
27
            for j in range(self.y, self.y + self.h):
 
28
                grid[j + 1][i + 1] = self
 
29
        Block.total += 1
 
30
 
 
31
    def collide(self, ball):
 
32
        self.alive = False
 
33
        for i in range(self.x, self.x + self.w):
 
34
            for j in range(self.y, self.y + self.h):
 
35
                grid[j + 1][i + 1] = None
 
36
        Block.killed += 1
 
37
        return False
 
38
 
 
39
    def tick(self, win):
 
40
        if self.alive:
 
41
            for i in range(self.x, self.x + self.w):
 
42
                for j in range(self.y, self.y + self.h):
 
43
                    win.addch(j, i, curses.ACS_BLOCK, self.fmt)
 
44
        return self.alive
 
45
 
 
46
class Ball:
 
47
    alive = False
 
48
    killed = 0
 
49
 
 
50
    def __init__(self, x, y, vx, vy):
 
51
        self.x = x
 
52
        self.y = y
 
53
        self.vx = vx
 
54
        self.vy = vy
 
55
        Ball.alive = True
 
56
 
 
57
    def collide(self, ball):
 
58
        return True
 
59
 
 
60
    def encounter(self, dx, dy):
 
61
        ent = grid[self.y + dy + 1][self.x + dx + 1]
 
62
        if ent and not ent.collide(self):
 
63
            self.vx -= 2 * dx
 
64
            self.vy -= 2 * dy
 
65
        return ent
 
66
 
 
67
    def tick(self, win):
 
68
        while self.y < ship.y:
 
69
            if self.encounter((self.vx + self.vy) / 2, (self.vy - self.vx) / 2):
 
70
                continue
 
71
            if self.encounter((self.vx - self.vy) / 2, (self.vy + self.vx) / 2):
 
72
                continue
 
73
            if self.encounter(self.vx, self.vy):
 
74
                continue
 
75
            break
 
76
        self.x += self.vx
 
77
        self.y += self.vy
 
78
        try:
 
79
            win.addch(self.y, self.x, 'O')
 
80
        except curses.error:
 
81
            Ball.alive = False
 
82
            Ball.killed += 1
 
83
        return Ball.alive
 
84
 
 
85
class Ship:
 
86
    def __init__(self, x, y):
 
87
        self.x = x
 
88
        self.y = y
 
89
        self.hw = 10
 
90
        self.v = 4
 
91
        self.last = 1
 
92
        self.update()
 
93
 
 
94
    def update(self):
 
95
        grid[self.y + 1] = (
 
96
            [ None ] * (self.x - self.hw + 1) +
 
97
            [ self ] * (self.hw * 2 + 1) +
 
98
            [ None ] * (width - self.x - self.hw)
 
99
        )
 
100
 
 
101
    def collide(self, ball):
 
102
        ball.vy = -1
 
103
        if ball.x > self.x + self.hw / 2:
 
104
            ball.vx = 1
 
105
        elif ball.x < self.x - self.hw / 2:
 
106
            ball.vx = -1
 
107
        return True
 
108
 
 
109
    def shift(self, i):
 
110
        self.last = i
 
111
        self.x += self.v * i
 
112
        if self.x - self.hw < 0:
 
113
            self.x = self.hw
 
114
        elif self.x + self.hw >= width:
 
115
            self.x = width - self.hw - 1
 
116
        self.update()
 
117
 
 
118
    def spawn(self):
 
119
        if not Ball.alive:
 
120
            entities.append(Ball(self.x, self.y - 1, self.last, -1))
 
121
 
 
122
    def tick(self, win):
 
123
        if not Ball.alive:
 
124
            win.addch(self.y - 1, self.x, 'O')
 
125
        win.addch(self.y, self.x - self.hw, curses.ACS_LTEE)
 
126
        for i in range(-self.hw + 1, self.hw):
 
127
            win.addch(curses.ACS_HLINE)
 
128
        win.addch(curses.ACS_RTEE)
 
129
        return True
 
130
 
 
131
class PowerOverwhelmingException(Exception):
 
132
    pass
 
133
 
 
134
def main(stdscr):
 
135
    global height, width, ship
 
136
 
 
137
    for i in range(1, 8):
 
138
        curses.init_pair(i, i, 0)
 
139
    curses.curs_set(0)
 
140
    curses.raw()
 
141
 
 
142
    height, width = stdscr.getmaxyx()
 
143
 
 
144
    if height < 15 or width < 30:
 
145
        raise PowerOverwhelmingException(
 
146
            "Your computer is not powerful enough to run 'arc anoid'. "
 
147
            "It must support at least 30 columns and 15 rows of next-gen "
 
148
            "full-color 3D graphics.")
 
149
 
 
150
    status = curses.newwin(1, width, 0, 0)
 
151
    height -= 1
 
152
    game = curses.newwin(height, width, 1, 0)
 
153
    game.nodelay(1)
 
154
    game.keypad(1)
 
155
 
 
156
    grid[:] = [ [ None for x in range(width + 2) ] for y in range(height + 2) ]
 
157
    wall = Wall()
 
158
    for x in range(width + 2):
 
159
        grid[0][x] = wall
 
160
    for y in range(height + 2):
 
161
        grid[y][0] = grid[y][-1] = wall
 
162
    ship = Ship(width / 2, height - 5)
 
163
    entities.append(ship)
 
164
 
 
165
    colors = [ 1, 3, 2, 6, 4, 5 ]
 
166
    h = height / 10
 
167
    for x in range(1, width / 7 - 1):
 
168
        for y in range(1, 7):
 
169
            entities.append(Block(x * 7, y * h + x / 2 % 2, 7, h, colors[y - 1]))
 
170
 
 
171
    while True:
 
172
        while select.select([ sys.stdin ], [], [], 0)[0]:
 
173
            key = game.getch()
 
174
            if key == curses.KEY_LEFT or key == ord('a') or key == ord('A'):
 
175
                ship.shift(-1)
 
176
            elif key == curses.KEY_RIGHT or key == ord('d') or key == ord('D'):
 
177
                ship.shift(1)
 
178
            elif key == ord(' '):
 
179
                ship.spawn()
 
180
            elif key == 0x1b or key == 3 or key == ord('q') or key == ord('Q'):
 
181
                return
 
182
 
 
183
        game.resize(height, width)
 
184
        game.erase()
 
185
        entities[:] = [ ent for ent in entities if ent.tick(game) ]
 
186
 
 
187
        status.hline(0, 0, curses.ACS_HLINE, width)
 
188
        status.addch(0, 2, curses.ACS_RTEE)
 
189
        status.addstr(' SCORE: ', curses.A_BOLD | curses.color_pair(4))
 
190
        status.addstr('%s/%s ' % (Block.killed, Block.total), curses.A_BOLD)
 
191
        status.addch(curses.ACS_VLINE)
 
192
        status.addstr(' DEATHS: ', curses.A_BOLD | curses.color_pair(4))
 
193
        status.addstr('%s ' % Ball.killed, curses.A_BOLD)
 
194
        status.addch(curses.ACS_LTEE)
 
195
 
 
196
        if Block.killed == Block.total:
 
197
            message = ' A WINNER IS YOU!! '
 
198
            i = int(time.time() / 0.8)
 
199
            for x in range(width):
 
200
                for y in range(6):
 
201
                    game.addch(height / 2 + y - 3 + (x / 8 + i) % 2, x,
 
202
                               curses.ACS_BLOCK,
 
203
                               curses.A_BOLD | curses.color_pair(colors[y]))
 
204
            game.addstr(height / 2, (width - len(message)) / 2, message,
 
205
                           curses.A_BOLD | curses.color_pair(7))
 
206
 
 
207
        game.refresh()
 
208
        status.refresh()
 
209
        time.sleep(0.05)
 
210
 
 
211
try:
 
212
    curses.wrapper(main)
 
213
    print ('You destroyed %s blocks out of %s with %s deaths.' %
 
214
        (Block.killed, Block.total, Ball.killed))
 
215
except PowerOverwhelmingException as e:
 
216
    print e