Webエンジニア susumuis の技術ブログ

このブログの内容は個人の見解であり、所属する組織の公式見解ではありません

DevQuiz PAC-MAN 解いてみた

仕事と家庭の事情のため、時間が取れず、締め切り前夜に4時間(徹夜ですがなにか?)だけ時間が取れて挑戦してみました。
結果は、ぼろぼろでしたorz

4時間でできたのはPAC-MANだけ。
事前にウォーミングアップと、しりとりはレベル3を手動で勝っていて、OAuthとPAC-MANをこの4時間で挑戦しようとしたのだけど、PAC-MANのみで終了という感じです。
そのPAC-MANも…仕様嫁おれって感じ。(xx;
1.5時間くらいでシミュレータはできたけど、敵の動きのアルゴリズムが問題と食い違っていて、仕様読み直して直してを繰り返しでした。最終的に以下のようなコードで何とかレベル1のみクリア。レベル2以降になると、まだどこか、違ってるみたい。

じゃ、コード貼りますね。
技術的にはCanvas使っています。便利ですね。
ちなみに、Chrome5でしかテストしていません。
上記状況(仕様読み間違えては修正)のため、ひどく突貫工事の後が残っています。とくにEnemy#moveをもっときれいにしたかったですね。


<html>
<head>
	<title>PACMAN</title>
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 
<script language="JavaScript">
GameCanvas = function(ctx) {
	this.ctx = ctx;
	this.drawText = function(text, x, y) {
		this.ctx.fillText(text, (x + 1) * 12, (y + 1) * 12);
	}
}

function $(id) {
	return document.getElementById(id);
}

Enemy = function(type, x, y) {
	this.isFirst = true;
	this.isJ = (type == "J");
	this.type = type;
	this.x = x;
	this.y = y;
	this.dir = 0 // 0:left, 1:up, 2:right, 3:down;
	this.draw = function(gcvs) {
		if (this.isJ) {
			gcvs.drawText("J", this.x, this.y);
		} else {
			gcvs.drawText(type, this.x, this.y);
		}
	}
	this.move = function() {
		if (this.isFirst) {
			this.isFirst = false;
			
			var dSide = this.nextPositionByDir(3);
			var lSide = this.nextPositionByDir(0);
			var uSide = this.nextPositionByDir(1);
			var rSide = this.nextPositionByDir(2);
			if (PACMAN.getTextAt(dSide.x, dSide.y) != "#") { this.dir = 3; }
			else if (PACMAN.getTextAt(lSide.x, lSide.y) != "#") { this.dir = 0; }
			else if (PACMAN.getTextAt(uSide.x, uSide.y) != "#") { this.dir = 1; }
			else if (PACMAN.getTextAt(rSide.x, rSide.y) != "#") { this.dir = 2; }
			var nextPos = this.nextPositionByDir(this.dir);
			this.x = nextPos.x;
			this.y = nextPos.y;
			return;
		}
		
		var lSide = this.nextPositionByDir(this.dir - 1);
		var fSide = this.nextPositionByDir(this.dir);
		var rSide = this.nextPositionByDir(this.dir + 1);
		var bSide = this.nextPositionByDir(this.dir + 2);
		var crossCount = 0;
		if (PACMAN.getTextAt(lSide.x, lSide.y) != "#") { crossCount++; }
		if (PACMAN.getTextAt(fSide.x, fSide.y) != "#") { crossCount++; }
		if (PACMAN.getTextAt(rSide.x, rSide.y) != "#") { crossCount++; }
		if (PACMAN.getTextAt(bSide.x, bSide.y) != "#") { crossCount++; }
		
		if (this.isJ) {
			// 交差点
			if (crossCount >= 3) {
				this.type = this.type == "R" ? "L" : "R";
			}
		}
		// 交差点から始まる場合を考慮
		if (this.type == "J") {
			this.type = "L";
		}
		if (crossCount == 1) {
			this.dir = (this.dir + 2) % 4
		}
		
		if (crossCount == 2) {
			var lSide = this.nextPositionByDir(this.dir - 1);
			var fSide = this.nextPositionByDir(this.dir);
			var rSide = this.nextPositionByDir(this.dir + 1);
			if (PACMAN.getTextAt(lSide.x, lSide.y) != "#") { this.dir = (this.dir - 1) % 4; if (this.dir < 0) {this.dir += 4; }}
			else if (PACMAN.getTextAt(fSide.x, fSide.y) != "#") { }
			else if (PACMAN.getTextAt(rSide.x, rSide.y) != "#") { this.dir = (this.dir + 1) % 4; }
		}
		
		if (crossCount >= 3) {
			switch (this.type) {
				case "V":
					var dx = PACMAN.player.x - this.x;
					var dy = PACMAN.player.y - this.y;
					if (dy > 0 && PACMAN.getTextAt(this.x, this.y + 1) != "#") { this.dir = 3; }
					else if (dy < 0 && PACMAN.getTextAt(this.x, this.y - 1) != "#") { this.dir = 1; }
					else if (dx > 0 && PACMAN.getTextAt(this.x + 1, this.y) != "#") { this.dir = 2; }
					else if (dx < 0 && PACMAN.getTextAt(this.x - 1, this.y) != "#") { this.dir = 0; }
					else if (PACMAN.getTextAt(this.x, this.y + 1) != "#") { this.dir = 3; }
					else if (PACMAN.getTextAt(this.x - 1, this.y) != "#") { this.dir = 0; }
					else if (PACMAN.getTextAt(this.x, this.y - 1) != "#") { this.dir = 1; }
					else if (PACMAN.getTextAt(this.x + 1, this.y) != "#") { this.dir = 2; }
					break;
				case "H":
					var dx = PACMAN.player.x - this.x;
					var dy = PACMAN.player.y - this.y;
					if (dx > 0 && PACMAN.getTextAt(this.x + 1, this.y) != "#") { this.dir = 2; }
					else if (dy > 0 && PACMAN.getTextAt(this.x, this.y + 1) != "#") { this.dir = 3; }
					else if (dy < 0 && PACMAN.getTextAt(this.x, this.y - 1) != "#") { this.dir = 1; }
					else if (dx < 0 && PACMAN.getTextAt(this.x - 1, this.y) != "#") { this.dir = 0; }
					else if (PACMAN.getTextAt(this.x, this.y + 1) != "#") { this.dir = 3; }
					else if (PACMAN.getTextAt(this.x - 1, this.y) != "#") { this.dir = 0; }
					else if (PACMAN.getTextAt(this.x, this.y - 1) != "#") { this.dir = 1; }
					else if (PACMAN.getTextAt(this.x + 1, this.y) != "#") { this.dir = 2; }
					break;
				case "L":
					var lSide = this.nextPositionByDir(this.dir - 1);
					var fSide = this.nextPositionByDir(this.dir);
					var rSide = this.nextPositionByDir(this.dir + 1);
					if (PACMAN.getTextAt(lSide.x, lSide.y) != "#") { this.dir = (this.dir - 1) % 4; if (this.dir < 0) {this.dir += 4; }}
					else if (PACMAN.getTextAt(fSide.x, fSide.y) != "#") { }
					else if (PACMAN.getTextAt(rSide.x, rSide.y) != "#") { this.dir = (this.dir + 1) % 4; }
					break;
				case "R":
					var lSide = this.nextPositionByDir(this.dir - 1);
					var fSide = this.nextPositionByDir(this.dir);
					var rSide = this.nextPositionByDir(this.dir + 1);
					if (PACMAN.getTextAt(rSide.x, rSide.y) != "#") { this.dir = (this.dir + 1) % 4; }
					else if (PACMAN.getTextAt(fSide.x, fSide.y) != "#") { }
					else if (PACMAN.getTextAt(lSide.x, lSide.y) != "#") { this.dir = (this.dir - 1) % 4; if (this.dir < 0) {this.dir += 4; }}
					break;
			
			}
		}
		var nextPos = this.nextPositionByDir(this.dir);
		this.x = nextPos.x;
		this.y = nextPos.y;
	}
	
	this.nextPositionByDir = function(dir) {
		dir = dir % 4;
		if (dir < 0) {dir += 4; }
		if (dir == 0) { return {x: this.x - 1, y: this.y}; }
		if (dir == 1) { return {x: this.x,     y: this.y - 1}; }
		if (dir == 2) { return {x: this.x + 1, y: this.y}; }
		if (dir == 3) { return {x: this.x,     y: this.y + 1}; }
		alert(dir);
	}
}

Player = function(x, y) {
		this.x = x;
		this.y = y;
		this.draw = function(gcvs) {
			gcvs.drawText("@", this.x, this.y);
		};
		this.moveLeft  = function() {if (PACMAN.getTextAt(this.x - 1, this.y) != "#") {this.x--; PACMAN.keyHistory += "h"; PACMAN.move()}};
		this.moveUp    = function() {if (PACMAN.getTextAt(this.x, this.y - 1) != "#") {this.y--; PACMAN.keyHistory += "k"; PACMAN.move()}};
		this.moveRight = function() {if (PACMAN.getTextAt(this.x + 1, this.y) != "#") {this.x++; PACMAN.keyHistory += "l"; PACMAN.move()}};
		this.moveDown  = function() {if (PACMAN.getTextAt(this.x, this.y + 1) != "#") {this.y++; PACMAN.keyHistory += "j"; PACMAN.move()}};
		this.stay  = function() {PACMAN.keyHistory += "."; PACMAN.move()};
}

Block = function(x, y) {
		this.x = x;
		this.y = y;
		this.draw = function(gcvs) {
			gcvs.drawText("#", this.x, this.y);
		};
}

Dot = function(x, y) {
		this.x = x;
		this.y = y;
		this.draw = function(gcvs) {
			gcvs.drawText(".", this.x, this.y);
		};
}

var PACMAN = new (function() {
	this.eatCount = 0;
	this.keyHistory = "";

	this.player = new Player(0,0);
	this.enemies = [];
	this.blockes = [];
	this.dots = [];
	
	this.getTextAt = function(x, y) {
		if (this.player.x == x && this.player.y == y) {
			return "@";
		}
		for (var i = 0; i < this.blockes.length; i++) {
			if (this.blockes[i].x == x && this.blockes[i].y == y) {
				return "#";
			}
		}
		for (var i = 0; i < this.enemies.length; i++) {
			if (this.enemies[i].x == x && this.enemies[i].y == y) {
				return this.enemies[i].type;
			}
		}
		for (var i = 0; i < this.dots.length; i++) {
			if (this.dots[i].x == x && this.dots[i].y == y) {
				return ".";
			}
		}
	}
	
	this.move = function() {
		for (var i = 0; i < this.enemies.length; i++) {
			this.enemies[i].move();
		}
		
		for (var i = 0; i < this.dots.length; i++) {
			if (this.player.x == this.dots[i].x && this.player.y == this.dots[i].y) {
				this.dots.splice(i, 1);
				this.eatCount++;
				break;
			}
		}
	};
	
	this.draw = function() {
		var ctx = $('canvas').getContext('2d');
		ctx.fillStyle = '#FFFFFF';
		ctx.fillRect(0,0,1000,1000);
		ctx.fillStyle = '#000000';
		ctx.font = '12px serif';
		var gcvs = new GameCanvas(ctx);
		this.player.draw(gcvs);
		for (var i = 0; i < this.blockes.length; i++) {
			this.blockes[i].draw(gcvs);
		}
		for (var i = 0; i < this.enemies.length; i++) {
			this.enemies[i].draw(gcvs);
		}
		for (var i = 0; i < this.dots.length; i++) {
			this.dots[i].draw(gcvs);
		}
		
		$('keyHistory').innerText = this.keyHistory;
		$('point').innerText = this.eatCount;
	};
	
	this.initialize = function(text) {
		this.eatCount = 0;
		this.keyHistory = "";
		this.player = null;
		this.blockes = [];
		this.enemies = [];
		this.dots = [];
		var x = 0;
		var y = 0;
		for (var i = 0; i < text.length; i++) {
			var c = text.charAt(i)
			if (c == "\r") {
				continue;
			}
			if (c == "\n") {
				y++;
				x = 0;
				continue;
			}
			if (c == "@") {
				this.player = new Player(x, y);
			}
			
			if (c == "#") {
				this.blockes.push(new Block(x, y));
			}
			
			if (c == ".") {
				this.dots.push(new Dot(x, y));
			}
			if (c == "V" || c == "H" || c == "L" || c == "R" || c == "J") {
				this.enemies.push(new Enemy(c, x, y));
			}			
			
			x++;
		}
		this.draw();
	}
})();
</script>
</head>
<body>
	key <span id="keyHistory"></span><br />
	point <span id="point"></span><br />
	<canvas id="canvas" width="800" height="400"></canvas><br />
	<textarea id="initData" cols="10" rows="10">
##########
#.....V..#
#.######.#
#...#....#
#L#...#.##
#.####..J#
#..@##.#.#
#.#....#.#
#.R.#.#H.#
##########
	</textarea><br />
	<a href="javascript:PACMAN.initialize($('initData').value);">initialize</a>
</body>
<script language="JavaScript">
	PACMAN.draw();
	window.onkeydown = function(e) {
		switch (e.keyCode) {
			case 32: PACMAN.player.stay(); break;
			case 37: PACMAN.player.moveLeft(); break;
			case 38: PACMAN.player.moveUp(); break;
			case 39: PACMAN.player.moveRight(); break;
			case 40: PACMAN.player.moveDown(); break;
		}
		PACMAN.draw();
	}
</script>
</html>