Skip to content

Instantly share code, notes, and snippets.

@jshbrntt
Created August 24, 2017 10:36
Show Gist options
  • Save jshbrntt/643977d2bb77f922eb0a7f54a3747beb to your computer and use it in GitHub Desktop.
Save jshbrntt/643977d2bb77f922eb0a7f54a3747beb to your computer and use it in GitHub Desktop.
import { Model } from "engine/mvc";
import { Signal } from "org/osflash/signals";
import { SoundAS } from "treefortress/sound";
import { TileModel } from "tile";
import { CellModel } from "cell";
import { GDTAssets } from "assets";
class GridModel extends Model {
protected _width: number;
protected _height: number;
protected _vector: Array<TileModel>;
protected _simulating: boolean;
protected _tilesFalling: number;
protected _swappedCell1: CellModel;
protected _swappedCell2: CellModel;
protected _tileMoved: Signal;
protected _tileAdded: Signal;
protected _tileRemoved: Signal;
protected _randomized: Signal;
protected _checked: Signal;
protected _swapped: Signal;
protected _simulated: Signal;
constructor(width: number, height: number) {
super();
this._width = width;
this._height = height;
this._vector = new Array<TileModel>(this._width * this._height);
this._simulating = false;
this._tilesFalling = 0;
this._swappedCell1 = null;
this._swappedCell2 = null;
this._tileMoved = new Signal();
this._tileAdded = new Signal();
this._tileRemoved = new Signal();
this._randomized = new Signal();
this._checked = new Signal();
this._swapped = new Signal();
this._simulated = new Signal();
}
public beginSimulation(): void {
console.log("simulate");
if (this._simulating) {
return;
}
this._simulating = true;
this.check();
}
protected check(): void {
console.log("check");
let matches: Array<CellModel> = this.getMatches();
this._checked.dispatch(matches.length);
if (matches.length > 0) {
this.remove(matches);
} else {
if (!this.fill()) {
this.endSimulation();
}
}
}
protected remove(matches: Array<CellModel>): void {
console.log("remove");
for (let match in matches) {
this.removeTile(match);
}
this.gravity();
SoundAS.play(GDTAssets.SOUND_SMASH + this.randomTileValue(1, 4), 0.5);
}
public gravity(): void {
console.log("gravity");
let movedTile: boolean = false;
for (
let i: number = this._vector.length - this._width;
i < this._vector.length;
i++
) {
let drop: number = 0;
let cell: CellModel = this.convert1D2D(i);
while (cell.y >= 0) {
if (!this.getTileModel(cell)) {
drop++;
} else if (drop) {
this._tilesFalling++;
this.moveTile(
cell,
new CellModel(cell.x, cell.y + drop),
this.onTileFallen
);
movedTile = true;
}
cell.y--;
}
}
if (!movedTile) {
if (!this.fill()) {
this.check();
}
}
}
protected onTileFallen(): void {
this._tilesFalling--;
if (this._tilesFalling == 0) {
if (!this.fill()) {
this.check();
}
}
SoundAS.play(GDTAssets.SOUND_GLASS + this.randomTileValue(1, 5), 0.54);
}
protected fill(): boolean {
console.log("this.fill");
let filled: boolean = false;
for (let i: number = this._width; i <= this._width * 2; i++) {
let cell: CellModel = this.convert1D2D(i);
if (!this.getTileModel(cell)) {
this.addTile(
this.createRandomTileModel(new CellModel(cell.x, cell.y - 1))
);
filled = true;
}
}
if (filled) {
this.gravity();
}
return filled;
}
public endSimulation(): void {
console.log("simulated");
this._simulated.dispatch();
this._simulating = false;
}
public swapCells(cell1: CellModel, cell2: CellModel): boolean {
let tile1: TileModel = this.getTileModel(cell1);
let tile2: TileModel = this.getTileModel(cell2);
if (!tile1 || !tile2 || this._simulating) {
return false;
}
// After first swap remove matches.
this._swappedCell1 = tile1.cell;
this._swappedCell2 = tile2.cell;
this.swapTiles(tile1, tile2);
return true;
}
protected swapTiles(tile1: TileModel, tile2: TileModel): void {
tile1.swap(tile2, this.onSwapped);
this.setTileModel(tile1.cell, tile1);
this.setTileModel(tile2.cell, tile2);
}
protected onSwapped(tile1: TileModel, tile2: TileModel): void {
if (
!tile1.cell.equals(this._swappedCell1) &&
!tile2.cell.equals(this._swappedCell2)
) {
SoundAS.play(GDTAssets.SOUND_SWAP, 0.5);
this._checked.addOnce(function(matches: number): void {
if (!matches) {
this.swapTiles(tile1, tile2);
} else {
this._swapped.dispatch();
}
});
this.beginSimulation();
} else {
this._swapped.dispatch();
SoundAS.play(GDTAssets.SOUND_UNDO, 1.0);
}
}
public moveTile(
fromCell: CellModel,
toCell: CellModel,
onMoved: Function = null
): void {
let movingTile: TileModel = this.getTileModel(fromCell);
if (movingTile) {
this.removeTile(toCell);
this.setTileModel(toCell, movingTile);
movingTile.move(toCell, onMoved);
this.setTileModel(fromCell, null);
this._tileMoved.dispatch(fromCell, toCell);
}
}
public addTile(tileModel: TileModel): void {
if (tileModel) {
this.setTileModel(tileModel.cell, tileModel);
this._tileAdded.dispatch(tileModel);
}
}
public removeTile(fromCell: CellModel): void {
let removedModel: TileModel = this.getTileModel(fromCell);
if (removedModel) {
removedModel.removed.dispatch();
this.setTileModel(fromCell, null);
this._tileRemoved.dispatch(removedModel);
}
}
public setTileModel(p: CellModel, v: TileModel): boolean {
let i: number = this.convert2D1D(p);
if (i < 0) {
return false;
}
this._vector[i] = v;
return true;
}
public getTileModel(p: CellModel): TileModel {
let i: number = this.convert2D1D(p);
if (i < 0) {
return null;
}
return this._vector[i];
}
public randomize(): void {
for (let i: number = this._width; i < this._vector.length; ++i) {
let currentCell: CellModel;
do {
currentCell = this.convert1D2D(i);
this._vector[i] = this.createRandomTileModel(currentCell);
} while (
this.matchedHeight(currentCell).length > 2 ||
this.matchedWidth(currentCell).length > 2
);
}
this._randomized.dispatch();
}
protected createRandomTileModel(cell: CellModel): TileModel {
return new TileModel(this.randomTileValue(0, 4), cell);
}
protected randomTileValue(min: number, max: number): number {
return Math.floor(Math.random() * (1 + max - min)) + min;
}
public convert1D2D(i: number): CellModel {
if (i < 0 || i > this._vector.length - 1) {
return null;
}
return new CellModel(i % this._width, Math.floor(i / this._width));
}
public convert2D1D(tile: CellModel): number {
if (
!tile ||
tile.x < 0 ||
tile.x > this._width - 1 ||
tile.y < 0 ||
tile.y > this._height - 1
) {
return -1;
}
return tile.x + tile.y * this._width;
}
protected unique(cells: Array<CellModel>): Array<CellModel> {
let unique: Array<CellModel> = cells.concat();
for (let i: number = 0; i < unique.length; ++i) {
for (let j: number = i + 1; j < unique.length; ++j) {
if (unique[i].equals(unique[j])) unique.splice(j--, 1);
}
}
return unique;
}
protected getMatches(): Array<CellModel> {
let matches: Array<CellModel> = new Array<CellModel>();
for (let i: number = 0; i < this._vector.length; ++i) {
let p: CellModel = this.convert1D2D(i);
let horizontalMatch: Array<CellModel> = this.matchedWidth(p);
if (horizontalMatch.length > 2) {
matches = matches.concat(horizontalMatch);
}
let verticalMatch: Array<CellModel> = this.matchedHeight(p);
if (verticalMatch.length > 2) {
matches = matches.concat(verticalMatch);
}
}
return this.unique(matches);
}
protected matchedHeight(p: CellModel): Array<CellModel> {
let matches: Array<CellModel> = new Array<CellModel>();
let tileModel: TileModel = this.getTileModel(p);
if (!tileModel) {
return matches;
}
matches.push(p);
let cursor: CellModel = new CellModel(p.x, p.y - 1);
while (this.valueMatches(tileModel, cursor)) {
matches.push(new CellModel(cursor.x, cursor.y));
cursor.y--;
}
cursor.y = p.y + 1;
while (this.valueMatches(tileModel, cursor)) {
matches.push(new CellModel(cursor.x, cursor.y));
cursor.y++;
}
return matches;
}
protected matchedWidth(p: CellModel): Array<CellModel> {
let matches: Array<CellModel> = new Array<CellModel>();
let tileModel: TileModel = this.getTileModel(p);
if (!tileModel) {
return matches;
}
matches.push(p);
let cursor: CellModel = new CellModel(p.x - 1, p.y);
while (this.valueMatches(tileModel, cursor)) {
matches.push(new CellModel(cursor.x, cursor.y));
cursor.x--;
}
cursor.x = p.x + 1;
while (this.valueMatches(tileModel, cursor)) {
matches.push(new CellModel(cursor.x, cursor.y));
cursor.x++;
}
return matches;
}
protected valueMatches(tileModel: TileModel, p: CellModel): boolean {
if (!tileModel || !this.getTileModel(p)) {
return false;
}
return tileModel.value == this.getTileModel(p).value;
}
public toString(): String {
let string: String = "";
for (let i: number = 0; i < this._vector.length; ++i) {
let tileModel: TileModel = this.getTileModel(this.convert1D2D(i));
string += !tileModel ? "X" : tileModel.value.toString();
if ((i + 1) % this._width == 0) {
string += "\n";
}
}
return string;
}
public get width(): number {
return this._width;
}
public get height(): number {
return this._height;
}
public get tileMoved(): Signal {
return this._tileMoved;
}
public get tileAdded(): Signal {
return this._tileAdded;
}
public get simulated(): Signal {
return this._simulated;
}
public get swapped(): Signal {
return this._swapped;
}
public get simulating(): boolean {
return this._simulating;
}
public get checked(): Signal {
return this._checked;
}
public get tileRemoved(): Signal {
return this._tileRemoved;
}
public get randomized(): Signal {
return this._randomized;
}
public get size(): number {
return this._vector.length;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment