forked from: minesweeper
// forked from wh0's minesweeper
package {
import net.user1.reactor.*;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.text.TextField;
import flash.text.TextFormat;
import flash.ui.Keyboard;
import flash.text.TextFormatAlign;
public class Minesweeper extends Sprite {
private static const WIDTH:int = 30;
private static const HEIGHT:int = 16;
private static const NUM_MINES:int = 99;
private static const SIZE:int = 15;
private static const DX:int = 8;
private static const DY:int = 42;
private static const LEFT:int = 90; // Z
private static const RIGHT:int = 88; // X
private static const MINES_AROUND:int = 0xf;
private static const MARKS_AROUND:int = 0xf0;
private static const MINE:int = 0x100;
private static const OPEN:int = 0x200;
private static const MARK:int = 0x400;
private const FIELD:Vector.<Vector.<int>> = new Vector.<Vector.<int>>(HEIGHT, true);
private const BITMAP:BitmapData = new BitmapData(WIDTH * SIZE, HEIGHT * SIZE, false);
private const GRAPHICS:BitmapData = new BitmapData(180, 15, false);
private const DEBUG:TextField = new TextField();
private const REACTOR:Reactor = new Reactor();
private var ROOM:Room;
private var row:int = -1;
private var col:int = -1;
private var left:Boolean = false;
private var right:Boolean = false;
private var game:int = 0;
private var score:int = 0;
public function Minesweeper() {
prepareGraphics();
for (var r:int = 0; r < HEIGHT; r++)
FIELD[r] = new Vector.<int>(WIDTH, true);
var b:Bitmap = new Bitmap(BITMAP);
b.x = DX;
b.y = DY;
addChild(b);
DEBUG.width = 339;
DEBUG.height = 144;
DEBUG.x = 126;
DEBUG.y = 321;
addChild(DEBUG);
REACTOR.addEventListener(ReactorEvent.READY, ready);
REACTOR.connect('tryunion.com', 80);
}
private function prepareGraphics():void {
var t:TextField = new TextField(); addChild(t); t.x = 1; t.y = -1; t.width = 14;
t.defaultTextFormat = new TextFormat('Arial', 11, null, true, null, null, null, null, TextFormatAlign.CENTER);
var m:Matrix = new Matrix();
graphics.beginFill(0xc0c0c0); graphics.drawRect(0, 0, 15, 15); graphics.endFill();
graphics.lineStyle(1, 0x808080); graphics.moveTo(15, 0); graphics.lineTo(0, 0); graphics.lineTo(0, 15);
GRAPHICS.draw(this, m);
t.textColor = 0x0000ff; t.text = '1'; m.translate(15, 0); GRAPHICS.draw(this, m);
t.textColor = 0x008000; t.text = '2'; m.translate(15, 0); GRAPHICS.draw(this, m);
t.textColor = 0xff0000; t.text = '3'; m.translate(15, 0); GRAPHICS.draw(this, m);
t.textColor = 0x000080; t.text = '4'; m.translate(15, 0); GRAPHICS.draw(this, m);
t.textColor = 0x800000; t.text = '5'; m.translate(15, 0); GRAPHICS.draw(this, m);
t.textColor = 0x008080; t.text = '6'; m.translate(15, 0); GRAPHICS.draw(this, m);
t.textColor = 0x808080; t.text = '7'; m.translate(15, 0); GRAPHICS.draw(this, m);
t.textColor = 0x000000; t.text = '8'; m.translate(15, 0); GRAPHICS.draw(this, m);
removeChild(t); graphics.clear();
graphics.beginFill(0xc0c0c0); graphics.drawRect(0, 0, 15, 15); graphics.endFill();
graphics.lineStyle(1, 0xffffff); graphics.moveTo(0, 14); graphics.lineTo(0, 0); graphics.lineTo(14, 0);
graphics.moveTo(1, 13); graphics.lineTo(1, 1); graphics.lineTo(13, 1);
graphics.lineStyle(1, 0x808080); graphics.moveTo(1, 14); graphics.lineTo(14, 14); graphics.lineTo(14, 1);
graphics.moveTo(2, 13); graphics.lineTo(13, 13); graphics.lineTo(13, 2);
m.translate(15, 0); GRAPHICS.draw(this, m);
graphics.lineStyle(); graphics.beginFill(0xff0000); graphics.moveTo(9, 3); graphics.lineTo(4, 5); graphics.lineTo(9, 8); graphics.endFill();
graphics.beginFill(0x000000); graphics.moveTo(9, 9); graphics.lineTo(11, 12); graphics.lineTo(4, 12); graphics.endFill();
m.translate(15, 0); GRAPHICS.draw(this, m);
graphics.clear();
graphics.beginFill(0xff0000); graphics.drawRect(0, 0, 15, 15); graphics.endFill();
graphics.lineStyle(1, 0x808080); graphics.moveTo(15, 0); graphics.lineTo(0, 0); graphics.lineTo(0, 15);
graphics.lineStyle(); graphics.beginFill(0x000000); graphics.drawCircle(8, 8, 4); graphics.endFill();
m.translate(15, 0); GRAPHICS.draw(this, m);
graphics.clear();
}
private function ready(e:ReactorEvent):void {
DEBUG.appendText('connected\n');
REACTOR.self().setAttribute('iconURL', loaderInfo.parameters['viewer.iconURL']);
REACTOR.self().setAttribute('displayName', loaderInfo.parameters['viewer.displayName']);
REACTOR.self().setAttribute('s', '0');
ROOM = REACTOR.getRoomManager().createRoom('ms');
ROOM.addEventListener(RoomEvent.JOIN, joinRoom);
ROOM.join();
}
private function joinRoom(e:RoomEvent):void {
DEBUG.appendText('joined\n');
var a:Object = ROOM.getAttributes();
var i:int;
if (a.g) {
loadGame(a.g.split(','));
for (var n:String in a) {
if (n.charAt(0) == 'o' && parseInt(a[n], 10) == game) {
i = parseInt(n.substr(1), 10);
var c:int = i % WIDTH;
var r:int = i / WIDTH;
simpleOpen(r, c);
}
}
} else {
createGame();
}
var o:Array = ROOM.getOccupants();
for (i = 0; i < o.length; i++)
addChild(new ClientDisplay(o[i]));
ROOM.addEventListener(AttributeEvent.UPDATE, attrUpdate);
ROOM.addEventListener(RoomEvent.ADD_OCCUPANT, addOcc);
ROOM.addEventListener(RoomEvent.REMOVE_OCCUPANT, removeOcc);
ROOM.addEventListener(RoomEvent.UPDATE_CLIENT_ATTRIBUTE, updateClientAttr);
stage.addEventListener(MouseEvent.MOUSE_MOVE, mouse);
stage.addEventListener(KeyboardEvent.KEY_DOWN, keydown);
stage.addEventListener(KeyboardEvent.KEY_UP, keyup);
}
private function clear():void {
for (var r:int = 0; r < HEIGHT; r++) {
for (var c:int = 0; c < WIDTH; c++) {
FIELD[r][c] = 0;
cell(r, c, 9);
}
}
}
private function loadGame(a:Array):void {
var i:int = parseInt(a[0], 10);
if (i <= game)
return;
game = i;
DEBUG.appendText('loading game ' + game + '\n');
clear();
for (var r:int = 0; r < HEIGHT; r++) {
i = parseInt(a[r + 1], 10);
for (var c:int = WIDTH - 1; c >= 0; c--) {
if (i & 1) {
FIELD[r][c] |= MINE;
propagate(incMines, r, c);
}
i >>= 1;
}
}
score = 0;
REACTOR.self().setAttribute('s', '0');
}
private function createGame():void {
DEBUG.appendText('creating game ' + (++game).toString() + '\n');
clear();
var i:int = 0;
while (i < NUM_MINES) {
var r:int = Math.random() * HEIGHT;
var c:int = Math.random() * WIDTH;
if (FIELD[r][c] & MINE)
continue;
FIELD[r][c] |= MINE;
i++;
propagate(incMines, r, c);
}
sendGame();
score = 0;
REACTOR.self().setAttribute('s', '0');
}
private function sendGame():void {
var a:String = game.toString();
for (var r:int = 0; r < HEIGHT; r++) {
var i:int = 0;
for (var c:int = 0; c < WIDTH; c++)
i = i << 1 | (FIELD[r][c] & MINE) >> 8;
a += ',' + i.toString();
}
ROOM.setAttribute('g', a);
}
private function attrUpdate(e:AttributeEvent):void {
var a:Attribute = e.getChangedAttr();
var r:int, c:int, i:int;
if (a.name.charAt(0) == 'o' && parseInt(a.value, 10) == game) {
i = parseInt(a.name.substr(1), 10);
c = i % WIDTH;
r = i / WIDTH;
simpleOpen(r, c);
} else if (a.name == 'g') {
loadGame(a.value.split(','));
}
}
private function addOcc(e:RoomEvent):void {
var a:IClient = e.getClient();
addChild(new ClientDisplay(a));
}
private function removeOcc(e:RoomEvent):void {
ClientDisplay.remove(e.getClient().getClientID());
}
private function updateClientAttr(e:RoomEvent):void {
var a:Attribute = e.getChangedAttr();
if (a.name == 's')
ClientDisplay.setScore(e.getClient().getClientID(), a.value);
}
private function sendOpen(r:int, c:int):void {
ROOM.setAttribute('o' + (r * WIDTH + c), game.toString());
REACTOR.self().setAttribute('s', score.toString());
}
private function simpleOpen(r:int, c:int):void {
if (!bound(r, c))
return;
if (FIELD[r][c] & OPEN)
return;
FIELD[r][c] |= OPEN;
if (FIELD[r][c] & MINE) {
cell(r, c, 11);
propagate(incMarks, r, c);
} else {
cell(r, c, FIELD[r][c] & MINES_AROUND);
}
}
private function mouse(e:MouseEvent):void {
if (left) {
release(row, col);
if (right)
propagate(release, row, col);
}
row = (e.stageY - DY) / SIZE;
col = (e.stageX - DX) / SIZE;
if (left) {
depress(row, col);
if (right)
propagate(depress, row, col);
}
}
private function keydown(e:KeyboardEvent):void {
if (e.keyCode == LEFT) {
left = true;
depress(row, col);
if (right)
propagate(depress, row, col);
} else if (e.keyCode == RIGHT) {
if (!right) {
right = true;
if (left) {
depress(row, col);
propagate(depress, row, col);
} else {
mark(row, col);
}
}
} else if (e.keyCode == Keyboard.F2) {
if (!left)
createGame();
}
}
private function keyup(e:KeyboardEvent):void {
if (e.keyCode == LEFT) {
if (left) {
left = false;
release(row, col);
if (right) {
propagate(release, row, col);
special(row, col);
} else {
open(row, col);
}
}
} else if (e.keyCode == RIGHT) {
right = false;
if (left) {
left = false;
release(row, col);
propagate(release, row, col);
special(row, col);
}
}
}
private function depress(r:int, c:int):void {
if (!bound(r, c))
return;
if (FIELD[r][c] & (OPEN | MARK))
return;
cell(r, c, 0);
}
private function release(r:int, c:int):void {
if (!bound(r, c))
return;
if (FIELD[r][c] & (OPEN | MARK))
return;
cell(r, c, 9);
}
private function open(r:int, c:int):void {
if (!bound(r, c))
return;
if (FIELD[r][c] & (OPEN | MARK))
return;
FIELD[r][c] |= OPEN;
if (FIELD[r][c] & MINE) {
cell(r, c, 11);
score -= 30;
propagate(incMarks, r, c);
sendOpen(r, c);
} else {
cell(r, c, FIELD[r][c] & MINES_AROUND);
score++;
sendOpen(r, c);
if ((FIELD[r][c] & MINES_AROUND) == 0)
propagate(open, r, c);
}
}
private function mark(r:int, c:int):void {
if (!bound(r, c))
return;
if (FIELD[r][c] & OPEN)
return;
if (FIELD[r][c] & MARK) {
cell(r, c, 9);
FIELD[r][c] &= ~MARK;
propagate(decMarks, r, c);
} else {
cell(r, c, 10);
FIELD[r][c] |= MARK;
propagate(incMarks, r, c);
}
}
private function special(r:int, c:int):void {
if (!bound(r, c))
return;
if (!(FIELD[r][c] & OPEN))
return;
if ((FIELD[r][c] & MINES_AROUND) != (FIELD[r][c] & MARKS_AROUND) >> 4)
return;
propagate(open, r, c);
}
private function incMines(r:int, c:int):void {
if (!bound(r, c))
return;
FIELD[r][c] += 0x1;
}
private function incMarks(r:int, c:int):void {
if (!bound(r, c))
return;
FIELD[r][c] += 0x10;
}
private function decMarks(r:int, c:int):void {
if (!bound(r, c))
return;
FIELD[r][c] -= 0x10;
}
private function propagate(a:Function, r:int, c:int):void {
a(r - 1, c - 1);
a(r - 1, c );
a(r - 1, c + 1);
a(r , c - 1);
a(r , c + 1);
a(r + 1, c - 1);
a(r + 1, c );
a(r + 1, c + 1);
}
private function bound(r:int, c:int):Boolean {
return r >= 0 && r < HEIGHT && c >= 0 && c < WIDTH;
}
private function cell(r:int, c:int, i:int):void {
BITMAP.copyPixels(GRAPHICS, new Rectangle(i * SIZE, 0, SIZE, SIZE), new Point(c * SIZE, r * SIZE));
}
}
}
import net.user1.reactor.*;
import flash.display.Sprite;
import flash.display.Loader;
import flash.net.URLRequest;
import flash.text.TextField;
import flash.text.TextFormat;
import flash.text.TextFormatAlign;
import flash.events.Event;
class ClientDisplay extends Sprite {
private static const instances:Object = new Object();
private static var tail:ClientDisplay;
private static var count:int = 0;
private var icon:Loader;
private var nameField:TextField;
private var scoreField:TextField;
private var prev:ClientDisplay;
private var next:ClientDisplay;
public function ClientDisplay(client:IClient) {
instances[client.getClientID()] = this;
prev = tail;
if (tail)
tail.next = this;
tail = this;
icon = new Loader();
icon.load(new URLRequest(client.getAttribute('iconURL')));
addChild(icon);
var f:TextFormat = new TextFormat();
f.align = TextFormatAlign.CENTER;
nameField = new TextField();
nameField.defaultTextFormat = f;
nameField.width = 100;
nameField.height = 16;
nameField.y = 104;
nameField.text = client.getAttribute('displayName');
addChild(nameField);
scoreField = new TextField();
scoreField.defaultTextFormat = f;
scoreField.width = 100;
scoreField.height = 16;
scoreField.y = 124;
scoreField.text = client.getAttribute('s');
addChild(scoreField);
x = count++ * 113 + 13;
y = 321;
}
public static function remove(id:String):void {
var o:ClientDisplay = instances[id];
if (o == null)
return;
if (o.prev)
o.prev.next = o.next;
if (o.next)
o.next.prev = o.prev;
if (tail == o)
tail = o.prev;
o.parent.removeChild(o);
for (o = o.next; o; o = o.next) {
o.x -= 113;
}
count--;
}
public static function setScore(id:String, score:String):void {
var o:ClientDisplay = instances[id];
if (o == null)
return;
o.scoreField.text = score;
}
}