Skip to content

Instantly share code, notes, and snippets.

@takatama
Created February 19, 2013 16:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save takatama/4987698 to your computer and use it in GitHub Desktop.
Save takatama/4987698 to your computer and use it in GitHub Desktop.
TypeScript入門 ぷよ編
ぷよ http://ja.wikipedia.org/wiki/%E3%81%B7%E3%82%88%E3%81%B7%E3%82%88
<左側のプレーヤー>
a 左移動
d 右移動
s 右回転
x 高速落下
<右側のプレーヤー>
左右キー 移動
上キー 右回転
下キー 高速落下
<div id="player1" style="float:left">
<canvas id="canvas1" width="200" height="340"></canvas>
<p>
a 左, d 右, s 回転, x 落下
</p>
</div>
<div id="space" style="float:left">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</div>
<div id="player2" style="float:left">
<canvas id="canvas2" width="200" height="340"></canvas>
<p>
←左, →右, ↑回転, ↓落下
</p>
</div>
<br style="clear:left">
<button id="start">start</button>
<button id="stop">stop</button>
<button id="restart">restart</button>
<button id="oneplay">一人で遊ぶ</button>
<button id="twoplay">二人で遊ぶ</button>
//TODO ちぎれたときのコンボの数え方が間違ってる?
class Field {
cellSize: number;
context;
maxX: number = 6;
maxY: number = 12;
topPuyos: number[] = [];
puyoPair: PuyoPair;
nextPair: PuyoPair;
fixedPuyos: Puyo[] = [];
timer: number;
combo = -1;
oppositeField: Field;
jammers: Puyo[] = [];
keyController = null;
message: string;
messageColor: string;
constructor(public canvas: HTMLCanvasElement) {
this.context = canvas.getContext('2d');
this.cellSize = canvas.width / (this.maxX + 1);
this.init();
}
init(): Field {
var i;
this.stop();
this.puyoPair = null;
this.nextPair = null;
this.fixedPuyos = [];
this.jammers = [];
this.combo = -1;
for (i = 0; i < this.maxX; i++) {
this.topPuyos[i] = this.maxY;
}
if (this.keyController) {
document.removeEventListener('keydown', this.keyController, false);
this.keyController = null;
}
this.nextPuyoPair();
return this;
}
start(): Field {
var that = this;
if (this.timer) {
return;
}
this.draw();
this.timer = setInterval(function () {
var hit = false;
that.puyoPair.down();
that.draw();
hit = that.hitting();
that.combine();
if (hit) {
that.nextPuyoPair();
that.draw();
}
}, 1000);
if (!this.keyController) {
this.keyController = this.createKeyController();
document.addEventListener('keydown', this.keyController, false);
}
return this;
}
stop(): Field {
clearInterval(this.timer);
this.timer = null;
return this;
}
clear(): Field {
this.init();
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
return this;
}
draw(): void {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.context.strokeRect(0, 0, this.canvas.width - this.cellSize, this.canvas.height);
if (this.puyoPair) {
this.puyoPair.draw(this.context, this.cellSize);
}
var that = this;
this.fixedPuyos.forEach(function (puyo) {
puyo.draw(that.context, that.cellSize);
});
this.nextPair.draw(this.context, this.cellSize);
this.drawString();
}
hitting(): bool {
if (this.puyoPair.inPuyo.y > this.topPuyos[this.puyoPair.inPuyo.x] - 2 ||
this.puyoPair.outPuyo.y > this.topPuyos[this.puyoPair.outPuyo.x] - 2 ) {
this.combo = -1;
this.fixPuyoPairs();
return true;
}
return false;
}
fixPuyoPairs(): void {
var belowPuyo = this.puyoPair.inPuyo,
abovePuyo = this.puyoPair.outPuyo,
temp;
if (belowPuyo.y < abovePuyo.y) {
temp = belowPuyo;
belowPuyo = abovePuyo;
abovePuyo = temp;
}
this.fixAtTop(belowPuyo);
this.fixAtTop(abovePuyo);
}
fixAtTop(puyo: Puyo) {
puyo.y = this.topPuyos[puyo.x] - 1;
this.fixedPuyos.push(puyo);
this.topPuyos[puyo.x] -= 1;
if (this.topPuyos[puyo.x] <= 0) {
this.lose();
}
}
lose(): void {
this.stop();
this.displayString('Lose', 'blue');
if (this.oppositeField) {
this.oppositeField.stop();
this.oppositeField.displayString('Win', 'red');
}
}
displayString(message: string, color: string): void {
this.message = message;
this.messageColor = color;
this.draw();
}
drawString(): void {
if (this.message) {
var oldStyle = this.context.strokeStyle;
this.context.strokeStyle = this.messageColor;
this.context.textAlign = 'center';
this.context.font = 'normal 20pt Calibri';
this.context.strokeText(this.message, this.cellSize * this.maxX / 2, this.cellSize * this.maxY / 2);
this.context.strokeStyle = oldStyle;
}
}
randomPuyoPair(): PuyoPair {
this.randomColor();
var pair = new PuyoPair(this, new Puyo(this.maxX, 1, this.randomColor()), new Puyo(this.maxX, 0, this.randomColor()));
return pair;
}
randomColor(): string {
switch (Math.floor(Math.random() * 4)) {
case 0:
return "blue";
case 1:
return "orange";
case 2:
return "green";
case 3:
return "purple";
}
}
nextPuyoPair(): void {
if (!this.nextPair) {
this.nextPair = this.randomPuyoPair();
}
this.puyoPair = this.nextPair;
// this.puyoPair.left().left().left();
if(this.fixedPuyo(3, 0) || this.fixedPuyo(3, 1)) {
this.lose();
}
this.puyoPair.inPuyo.x = 3;
this.puyoPair.outPuyo.x = 3;
this.nextPair = this.randomPuyoPair();
}
createKeyController() {
return null;
}
combine(): void {
var visitedPuyos: Puyo[] = [],
visitor = new CombineVisitor(this, visitedPuyos);
this.fixedPuyos.forEach(function (puyo) {
puyo.accept(visitor);
});
this.deleteCombinedPuyoPairs(visitor.combinedPuyoPairs);
}
fixedPuyo(x: number, y: number): Puyo {
var i, len;
if (x < 0 || x >= this.maxX || y < 0 || y >= this.maxY) {
return null;
}
for (i = 0, len = this.fixedPuyos.length; i < len; i++) {
if (this.fixedPuyos[i].x == x && this.fixedPuyos[i].y === y) {
return this.fixedPuyos[i];
}
}
return null;
}
deleteCombinedPuyoPairs(pairs: PuyoPair[][]): void {
var i, j, len;
this.combo++;
for (i = 0, len = pairs.length; i < len; i++) {
var deletingPuyos = this.puyos(pairs[i]);
for (j = 0; j < deletingPuyos.length; j++) {
this.deleteFixedPuyo(deletingPuyos[j]);
}
this.shiftDownFixedPuyos(deletingPuyos);
this.updateTopPuyos(deletingPuyos);
var jammer = deletingPuyos.length / 2 * (this.combo + 1);
if (this.oppositeField) {
this.oppositeField.addJammer(jammer);
}
this.draw();
}
}
puyos(pairs: PuyoPair[]) {
var i, j, puyos = [];
for (i = 0; i < pairs.length; i++) {
this.add(puyos, pairs[i].inPuyo);
this.add(puyos, pairs[i].outPuyo);
}
return puyos;
}
add(arr, obj): void {
var i;
for (i = 0; i < arr.length; i++) {
if (arr[i] === obj) {
return;
}
}
arr.push(obj);
}
deleteFixedPuyo(puyo: Puyo): void {
var i, len;
for (i = 0, len = this.fixedPuyos.length; i < len; i++) {
if (this.fixedPuyos[i] === puyo) {
this.fixedPuyos.splice(i, 1);
return;
}
}
}
shiftDownFixedPuyos(deletingPuyos: Puyo[]): void {
var i, len, newPuyos = [];
for(i = 0, len = this.fixedPuyos.length; i < len; i++) {
this.fixedPuyos[i].y += this.countBelowSpace(this.fixedPuyos[i].x, this.fixedPuyos[i].y, deletingPuyos);
}
}
countBelowSpace(x: number, y: number, deletingPuyos: Puyo[]): number {
var i, len, count = 0;
for (i = 0, len = deletingPuyos.length; i < len; i++) {
if (deletingPuyos[i].x === x && deletingPuyos[i].y > y) {
count++;
}
}
return count;
}
updateTopPuyos(deletingPuyos: Puyo[]): void {
var i;
for (i = 0; i < this.maxX; i++) {
var deleteCount = this.countBelowSpace(i, this.topPuyos[i] - 1, deletingPuyos);
this.topPuyos[i] += deleteCount;
}
}
addJammer(count: number): void {
var i;
for (i = 0; i < count; i++) {
var x = Math.floor(Math.random() * this.maxX);
this.jammers.push(new Puyo(x, 0, "gray"));
}
this.processJammer();
}
processJammer(): void {
var i;
for (i = 0; i < this.jammers.length; i++) {
this.fixAtTop(this.jammers[i]);
}
this.jammers = [];
this.draw();
}
}
class AlphabetField extends Field {
constructor(public canvas: HTMLCanvasElement) {
super(canvas);
}
createKeyController() {
var a = 65, w = 87, d = 68, s = 83, x = 88, z = 90;
var field = this;
return function (event) {
var chCode = (event.keyCode) ? event.keyCode : event.charCode;
switch(chCode) {
case w:
case s:
field.puyoPair.rotateRight();
field.draw();
break;
case d:
field.puyoPair.right();
field.draw();
break;
case a:
field.puyoPair.left();
field.draw();
break;
case z:
case x:
field.puyoPair.down();
field.draw();
break;
}
};
}
}
class ArrowField extends Field {
constructor(public canvas: HTMLCanvasElement) {
super(canvas);
}
createKeyController() {
var left = 37, up = 38, right = 39, down = 40;
var field = this;
return function (event) {
var chCode = (event.keyCode) ? event.keyCode : event.charCode;
switch(chCode) {
case up:
field.puyoPair.rotateRight();
field.draw();
break;
case right:
field.puyoPair.right();
field.draw();
break;
case left:
field.puyoPair.left();
field.draw();
break;
case down:
field.puyoPair.down();
field.draw();
break;
}
};
}
}
class CombineVisitor {
public combinedPuyoPairs: PuyoPair[][] = [];
constructor(private field: Field, private visited: Puyo[]) {
}
visit(puyo: Puyo): void {
var pairs: PuyoPair[] = [];
this.travel(puyo, pairs);
if (pairs.length >= 3) {
this.combinedPuyoPairs.push(pairs);
}
}
travel(puyo: Puyo, pairs: PuyoPair[]): void {
if (this.isVisited(puyo)) {
return;
}
this.visited.push(puyo);
this.pushPuyoPairIfCombined(puyo, this.field.fixedPuyo(puyo.x, puyo.y - 1), pairs);
this.pushPuyoPairIfCombined(puyo, this.field.fixedPuyo(puyo.x + 1, puyo.y), pairs);
this.pushPuyoPairIfCombined(puyo, this.field.fixedPuyo(puyo.x, puyo.y + 1), pairs);
this.pushPuyoPairIfCombined(puyo, this.field.fixedPuyo(puyo.x - 1, puyo.y), pairs);
}
isVisited(puyo: Puyo): bool {
var i, len;
for (i = 0, len = this.visited.length; i < len; i++) {
if (this.visited[i] === puyo) {
return true;
}
}
return false;
}
private pushPuyoPairIfCombined(puyoA: Puyo, puyoB: Puyo, pairs: PuyoPair[]): void {
if (!puyoA || !puyoB) {
return;
}
if (this.isVisited(puyoB)) {
return;
}
if (this.isCombinedTo(puyoA, puyoB)) {
pairs.push(new PuyoPair(this.field, puyoA, puyoB));
this.travel(puyoB, pairs);
}
}
isCombinedTo(puyoA: Puyo, puyoB: Puyo): bool {
if (puyoA.color === 'gray' || puyoB.color === 'gray') {
return false;
}
if (puyoA.color === puyoB.color) {
return true;
}
return false;
}
}
class Puyo {
constructor(public x: number, public y: number, public color: string) {
}
draw(context, cellSize: number) {
var radius = cellSize / 3;
context.beginPath();
context.fillStyle = this.color;
context.arc(this.xPx(cellSize), this.yPx(cellSize), radius, 0, Math.PI*2, false);
context.fill();
}
xPx(cellSize: number): number {
return this.x * cellSize + cellSize / 2;
}
yPx(cellSize: number): number {
return this.y * cellSize + cellSize / 2;
}
accept(visitor: CombineVisitor): void {
visitor.visit(this);
}
}
class PuyoPair {
constructor(private field: Field, public inPuyo: Puyo, public outPuyo: Puyo) {
}
draw(context, cellSize) {
/*
if (this.inPuyo.color === this.outPuyo.color) {
context.beginPath();
context.lineWidth = 5;
context.strokeStyle=this.inPuyo.color;
context.moveTo(this.inPuyo.xPx(cellSize), this.inPuyo.yPx(cellSize));
context.lineTo(this.outPuyo.xPx(cellSize), this.outPuyo.yPx(cellSize));
context.stroke();
}
*/
this.inPuyo.draw(context, cellSize);
this.outPuyo.draw(context, cellSize);
}
//TODO hitting with fixed puyos
rotateRight(): PuyoPair {
if (this.inPuyo.x === this.outPuyo.x) {
if (this.inPuyo.y > this.outPuyo.y) {
this.outPuyo.x += 1;
this.outPuyo.y += 1;
if (this.outPuyo.x >= this.field.maxX) {
this.left();
}
if (this.outPuyo.y >= this.field.maxY) {
this.up();
}
} else {
this.outPuyo.x -= 1;
this.outPuyo.y -= 1;
if (this.outPuyo.x < 0) {
this.right();
}
}
} else if (this.inPuyo.y === this.outPuyo.y) {
if (this.inPuyo.x > this.outPuyo.x) {
this.outPuyo.x += 1;
this.outPuyo.y -= 1;
if (this.outPuyo.x >= this.field.maxX) {
this.left();
}
} else {
this.outPuyo.x -= 1;
this.outPuyo.y += 1;
if (this.outPuyo.x < 0) {
this.right();
}
if (this.outPuyo.y >= this.field.maxY) {
this.up();
}
}
}
return this;
}
/*
rotateLeft(): void {
if (this.inPuyo.x === this.outPuyo.x) {
if (this.inPuyo.y > this.outPuyo.y) {
this.outPuyo.x -= 1;
this.outPuyo.y += 1;
} else {
this.outPuyo.x += 1;
this.outPuyo.y -= 1;
}
} else if (this.inPuyo.y === this.outPuyo.y) {
if (this.inPuyo.x > this.outPuyo.x) {
this.outPuyo.x += 1;
this.outPuyo.y += 1;
} else {
this.outPuyo.x -= 1;
this.outPuyo.y -= 1;
}
}
}
*/
right(): PuyoPair {
var maxX = Math.max(this.inPuyo.x, this.outPuyo.x);
if (maxX < this.field.maxX - 1
&& !this.field.fixedPuyo(maxX + 1, this.inPuyo.y) && !this.field.fixedPuyo(maxX + 1, this.outPuyo.y)) {
this.inPuyo.x += 1;
this.outPuyo.x += 1;
}
return this;
}
left(): PuyoPair {
var minX = Math.min(this.inPuyo.x, this.outPuyo.x);
if (minX > 0 && !this.field.fixedPuyo(minX - 1, this.inPuyo.y) && !this.field.fixedPuyo(minX - 1, this.outPuyo.y)) {
this.inPuyo.x -= 1;
this.outPuyo.x -= 1;
}
return this;
}
down(): PuyoPair {
var maxY = Math.max(this.inPuyo.y, this.outPuyo.y);
if (maxY < this.field.maxY - 1
&& !this.field.fixedPuyo(this.inPuyo.x, maxY + 1) && !this.field.fixedPuyo(this.outPuyo.x, maxY + 1)) {
this.inPuyo.y += 1;
this.outPuyo.y += 1;
}
return this;
}
up(): PuyoPair {
if (Math.min(this.inPuyo.y, this.outPuyo.y) > 0) {
this.inPuyo.y -= 1;
this.outPuyo.y -= 1;
}
return this;
}
}
var canvas1 = <HTMLCanvasElement>document.getElementById('canvas1');
var canvas2 = <HTMLCanvasElement>document.getElementById('canvas2');
var field1, field2;
function play(number) {
if (field1) {
field1.clear();
}
if (field2) {
field2.clear();
}
field1 = new AlphabetField(canvas1);
field1.init().start();
if (number > 1) {
field2 = new ArrowField(canvas2);
field1.oppositeField = field2;
field2.oppositeField = field1;
field2.init().start();
}
}
document.getElementById('start').onclick = function () {
if (field1) field1.start();
if (field2) field2.start();
}
document.getElementById('stop').onclick = function () {
if (field1) field1.stop();
if (field2) field2.stop();
}
document.getElementById('restart').onclick = function () {
if (field1) field1.init().start();
if (field2) field2.init().start();
}
document.getElementById('oneplay').onclick = function () {
play(1);
}
document.getElementById('twoplay').onclick = function () {
play(2);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment