Monthly Archives: 8月 2010

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>