1
0
Fork 0
mirror of https://we.phorge.it/source/arcanist.git synced 2024-11-25 16:22:42 +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:
Alan Huang 2012-07-21 02:09:21 -07:00
parent 1914bce11e
commit 316122c4e0
3 changed files with 248 additions and 0 deletions

202
scripts/breakout.py Executable file
View 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))

View file

@ -12,6 +12,7 @@ phutil_register_library_map(array(
array( array(
'ArcanistAliasWorkflow' => 'workflow/ArcanistAliasWorkflow.php', 'ArcanistAliasWorkflow' => 'workflow/ArcanistAliasWorkflow.php',
'ArcanistAmendWorkflow' => 'workflow/ArcanistAmendWorkflow.php', 'ArcanistAmendWorkflow' => 'workflow/ArcanistAmendWorkflow.php',
'ArcanistAnoidWorkflow' => 'workflow/ArcanistAnoidWorkflow.php',
'ArcanistApacheLicenseLinter' => 'lint/linter/ArcanistApacheLicenseLinter.php', 'ArcanistApacheLicenseLinter' => 'lint/linter/ArcanistApacheLicenseLinter.php',
'ArcanistApacheLicenseLinterTestCase' => 'lint/linter/__tests__/ArcanistApacheLicenseLinterTestCase.php', 'ArcanistApacheLicenseLinterTestCase' => 'lint/linter/__tests__/ArcanistApacheLicenseLinterTestCase.php',
'ArcanistBaseCommitParser' => 'parser/ArcanistBaseCommitParser.php', 'ArcanistBaseCommitParser' => 'parser/ArcanistBaseCommitParser.php',
@ -142,6 +143,7 @@ phutil_register_library_map(array(
array( array(
'ArcanistAliasWorkflow' => 'ArcanistBaseWorkflow', 'ArcanistAliasWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistAmendWorkflow' => 'ArcanistBaseWorkflow', 'ArcanistAmendWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistAnoidWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistApacheLicenseLinter' => 'ArcanistLicenseLinter', 'ArcanistApacheLicenseLinter' => 'ArcanistLicenseLinter',
'ArcanistApacheLicenseLinterTestCase' => 'ArcanistLinterTestCase', 'ArcanistApacheLicenseLinterTestCase' => 'ArcanistLinterTestCase',
'ArcanistBaseCommitParserTestCase' => 'ArcanistPhutilTestCase', 'ArcanistBaseCommitParserTestCase' => 'ArcanistPhutilTestCase',

View 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'
);
}
}