mirror of
https://we.phorge.it/source/arcanist.git
synced 2024-11-22 06:42:41 +01:00
Create a mysterious new workflow
Summary: Phabricator, as we all know, is marketed as a fun adventure game. However, while it is occasionally fun and often an adventure, it's so far been sorely deficient in the game aspect. This patch aims to rectify that oversight. (Presence of the first two qualities is not guaranteed.) Note: In case there's any doubt, this is not a serious suggestion. I was bored. Test Plan: Seriously? Reviewers: epriestley Reviewed By: epriestley CC: aran, Korvin Differential Revision: https://secure.phabricator.com/D3026
This commit is contained in:
parent
1914bce11e
commit
316122c4e0
3 changed files with 248 additions and 0 deletions
202
scripts/breakout.py
Executable file
202
scripts/breakout.py
Executable file
|
@ -0,0 +1,202 @@
|
|||
#!/usr/bin/python
|
||||
import sys
|
||||
import time
|
||||
import select
|
||||
import curses
|
||||
import curses.wrapper
|
||||
|
||||
entities = []
|
||||
grid = []
|
||||
|
||||
class Wall:
|
||||
def collide(self, ball):
|
||||
return False
|
||||
|
||||
class Block:
|
||||
killed = 0
|
||||
total = 0
|
||||
|
||||
def __init__(self, x, y, w, h, c):
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.w = w
|
||||
self.h = h
|
||||
self.fmt = curses.A_BOLD | curses.color_pair(c)
|
||||
self.alive = True
|
||||
for i in range(self.x, self.x + self.w):
|
||||
for j in range(self.y, self.y + self.h):
|
||||
grid[j + 1][i + 1] = self
|
||||
Block.total += 1
|
||||
|
||||
def collide(self, ball):
|
||||
self.alive = False
|
||||
for i in range(self.x, self.x + self.w):
|
||||
for j in range(self.y, self.y + self.h):
|
||||
grid[j + 1][i + 1] = None
|
||||
Block.killed += 1
|
||||
return False
|
||||
|
||||
def tick(self, win):
|
||||
if self.alive:
|
||||
for i in range(self.x, self.x + self.w):
|
||||
for j in range(self.y, self.y + self.h):
|
||||
win.addch(j, i, curses.ACS_BLOCK, self.fmt)
|
||||
return self.alive
|
||||
|
||||
class Ball:
|
||||
alive = False
|
||||
killed = 0
|
||||
|
||||
def __init__(self, x, y, vx, vy):
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.vx = vx
|
||||
self.vy = vy
|
||||
Ball.alive = True
|
||||
|
||||
def collide(self, ball):
|
||||
return True
|
||||
|
||||
def encounter(self, dx, dy):
|
||||
ent = grid[self.y + dy + 1][self.x + dx + 1]
|
||||
if ent and not ent.collide(self):
|
||||
self.vx -= 2 * dx
|
||||
self.vy -= 2 * dy
|
||||
return ent
|
||||
|
||||
def tick(self, win):
|
||||
while self.y < ship.y:
|
||||
if self.encounter((self.vx + self.vy) / 2, (self.vy - self.vx) / 2):
|
||||
continue
|
||||
if self.encounter((self.vx - self.vy) / 2, (self.vy + self.vx) / 2):
|
||||
continue
|
||||
if self.encounter(self.vx, self.vy):
|
||||
continue
|
||||
break
|
||||
self.x += self.vx
|
||||
self.y += self.vy
|
||||
try:
|
||||
win.addch(self.y, self.x, 'O')
|
||||
except curses.error:
|
||||
Ball.alive = False
|
||||
Ball.killed += 1
|
||||
return Ball.alive
|
||||
|
||||
class Ship:
|
||||
def __init__(self, x, y):
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.hw = 10
|
||||
self.v = 4
|
||||
self.last = 1
|
||||
self.update()
|
||||
|
||||
def update(self):
|
||||
grid[self.y + 1] = (
|
||||
[ None ] * (self.x - self.hw + 1) +
|
||||
[ self ] * (self.hw * 2 + 1) +
|
||||
[ None ] * (width - self.x - self.hw)
|
||||
)
|
||||
|
||||
def collide(self, ball):
|
||||
ball.vy = -1
|
||||
if ball.x > self.x + self.hw / 2:
|
||||
ball.vx = 1
|
||||
elif ball.x < self.x - self.hw / 2:
|
||||
ball.vx = -1
|
||||
return True
|
||||
|
||||
def shift(self, i):
|
||||
self.last = i
|
||||
self.x += self.v * i
|
||||
if self.x - self.hw < 0:
|
||||
self.x = self.hw
|
||||
elif self.x + self.hw >= width:
|
||||
self.x = width - self.hw - 1
|
||||
self.update()
|
||||
|
||||
def spawn(self):
|
||||
if not Ball.alive:
|
||||
entities.append(Ball(self.x, self.y - 1, self.last, -1))
|
||||
|
||||
def tick(self, win):
|
||||
if not Ball.alive:
|
||||
win.addch(self.y - 1, self.x, 'O')
|
||||
win.addch(self.y, self.x - self.hw, curses.ACS_LTEE)
|
||||
for i in range(-self.hw + 1, self.hw):
|
||||
win.addch(curses.ACS_HLINE)
|
||||
win.addch(curses.ACS_RTEE)
|
||||
return True
|
||||
|
||||
def main(stdscr):
|
||||
global height, width, ship
|
||||
|
||||
for i in range(1, 8):
|
||||
curses.init_pair(i, i, 0)
|
||||
curses.curs_set(0)
|
||||
curses.raw()
|
||||
height, width = stdscr.getmaxyx()
|
||||
status = curses.newwin(1, width, 0, 0)
|
||||
height -= 1
|
||||
game = curses.newwin(height, width, 1, 0)
|
||||
game.nodelay(1)
|
||||
game.keypad(1)
|
||||
|
||||
grid[:] = [ [ None for x in range(width + 2) ] for y in range(height + 2) ]
|
||||
wall = Wall()
|
||||
for x in range(width + 2):
|
||||
grid[0][x] = wall
|
||||
for y in range(height + 2):
|
||||
grid[y][0] = grid[y][-1] = wall
|
||||
ship = Ship(width / 2, height - 5)
|
||||
entities.append(ship)
|
||||
|
||||
colors = [ 1, 3, 2, 6, 4, 5 ]
|
||||
h = height / 10
|
||||
for x in range(1, width / 7 - 1):
|
||||
for y in range(1, 7):
|
||||
entities.append(Block(x * 7, y * h + x / 2 % 2, 7, h, colors[y - 1]))
|
||||
|
||||
while True:
|
||||
while select.select([ sys.stdin ], [], [], 0)[0]:
|
||||
key = game.getch()
|
||||
if key == curses.KEY_LEFT or key == ord('a') or key == ord('A'):
|
||||
ship.shift(-1)
|
||||
elif key == curses.KEY_RIGHT or key == ord('d') or key == ord('D'):
|
||||
ship.shift(1)
|
||||
elif key == ord(' '):
|
||||
ship.spawn()
|
||||
elif key == 0x1b or key == 3 or key == ord('q') or key == ord('Q'):
|
||||
return
|
||||
|
||||
game.resize(height, width)
|
||||
game.erase()
|
||||
entities[:] = [ ent for ent in entities if ent.tick(game) ]
|
||||
|
||||
status.hline(0, 0, curses.ACS_HLINE, width)
|
||||
status.addch(0, 2, curses.ACS_RTEE)
|
||||
status.addstr(' SCORE: ', curses.A_BOLD | curses.color_pair(4))
|
||||
status.addstr('%s/%s ' % (Block.killed, Block.total), curses.A_BOLD)
|
||||
status.addch(curses.ACS_VLINE)
|
||||
status.addstr(' DEATHS: ', curses.A_BOLD | curses.color_pair(4))
|
||||
status.addstr('%s ' % Ball.killed, curses.A_BOLD)
|
||||
status.addch(curses.ACS_LTEE)
|
||||
|
||||
if Block.killed == Block.total:
|
||||
message = ' A WINNER IS YOU!! '
|
||||
i = int(time.time() / 0.8)
|
||||
for x in range(width):
|
||||
for y in range(6):
|
||||
game.addch(height / 2 + y - 3 + (x / 8 + i) % 2, x,
|
||||
curses.ACS_BLOCK,
|
||||
curses.A_BOLD | curses.color_pair(colors[y]))
|
||||
game.addstr(height / 2, (width - len(message)) / 2, message,
|
||||
curses.A_BOLD | curses.color_pair(7))
|
||||
|
||||
game.refresh()
|
||||
status.refresh()
|
||||
time.sleep(0.05)
|
||||
|
||||
curses.wrapper(main)
|
||||
print ('You destroyed %s blocks out of %s with %s deaths.' %
|
||||
(Block.killed, Block.total, Ball.killed))
|
|
@ -12,6 +12,7 @@ phutil_register_library_map(array(
|
|||
array(
|
||||
'ArcanistAliasWorkflow' => 'workflow/ArcanistAliasWorkflow.php',
|
||||
'ArcanistAmendWorkflow' => 'workflow/ArcanistAmendWorkflow.php',
|
||||
'ArcanistAnoidWorkflow' => 'workflow/ArcanistAnoidWorkflow.php',
|
||||
'ArcanistApacheLicenseLinter' => 'lint/linter/ArcanistApacheLicenseLinter.php',
|
||||
'ArcanistApacheLicenseLinterTestCase' => 'lint/linter/__tests__/ArcanistApacheLicenseLinterTestCase.php',
|
||||
'ArcanistBaseCommitParser' => 'parser/ArcanistBaseCommitParser.php',
|
||||
|
@ -142,6 +143,7 @@ phutil_register_library_map(array(
|
|||
array(
|
||||
'ArcanistAliasWorkflow' => 'ArcanistBaseWorkflow',
|
||||
'ArcanistAmendWorkflow' => 'ArcanistBaseWorkflow',
|
||||
'ArcanistAnoidWorkflow' => 'ArcanistBaseWorkflow',
|
||||
'ArcanistApacheLicenseLinter' => 'ArcanistLicenseLinter',
|
||||
'ArcanistApacheLicenseLinterTestCase' => 'ArcanistLinterTestCase',
|
||||
'ArcanistBaseCommitParserTestCase' => 'ArcanistPhutilTestCase',
|
||||
|
|
44
src/workflow/ArcanistAnoidWorkflow.php
Normal file
44
src/workflow/ArcanistAnoidWorkflow.php
Normal file
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright 2012 Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @group workflow
|
||||
*/
|
||||
final class ArcanistAnoidWorkflow extends ArcanistBaseWorkflow {
|
||||
|
||||
public function getCommandSynopses() {
|
||||
return phutil_console_format(<<<EOTEXT
|
||||
**anoid**
|
||||
EOTEXT
|
||||
);
|
||||
}
|
||||
|
||||
public function getCommandHelp() {
|
||||
return phutil_console_format(<<<EOTEXT
|
||||
There's only one way to find out...
|
||||
EOTEXT
|
||||
);
|
||||
}
|
||||
|
||||
public function run() {
|
||||
phutil_passthru(
|
||||
dirname(phutil_get_library_root('arcanist')) . '/scripts/breakout.py'
|
||||
);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue