Created
September 4, 2019 18:48
-
-
Save courthead/3d41cc03edda1e596051129b445fca17 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class GameTreeNode { | |
constructor(obj = {}) { | |
this.parentNode = obj.parentNode || null; | |
this.upNode = null; | |
this.rightNode = null; | |
this.downNode = null; | |
this.leftNode = null; | |
this.minScore = obj.parentNode ? obj.parentNode.minScore + 1 : 0; | |
this.score = null; | |
this.dir = obj.dir || null; | |
} | |
getNextNode() { | |
if (!this.upNode || !this.upNode.score) { | |
if (!this.upNode) { | |
this.upNode = new GameTreeNode({ parentNode: this, dir: 'u' }); | |
} | |
return this.upNode; | |
} | |
if (!this.rightNode || !this.rightNode.score) { | |
if (!this.rightNode) { | |
this.rightNode = new GameTreeNode({ parentNode: this, dir: 'r' }); | |
} | |
return this.rightNode; | |
} | |
if (!this.downNode || !this.downNode.score) { | |
if (!this.downNode) { | |
this.downNode = new GameTreeNode({ parentNode: this, dir: 'd' }); | |
} | |
return this.downNode; | |
} | |
if (!this.leftNode || !this.leftNode.score) { | |
if (!this.leftNode) { | |
this.leftNode = new GameTreeNode({ parentNode: this, dir: 'l' }); | |
} | |
return this.leftNode; | |
} | |
return this.parentNode; | |
} | |
getMoveList() { | |
let str = this.dir; | |
let node = this; | |
while (node.parentNode) { | |
if (node.parentNode.dir) { | |
str = `${node.parentNode.dir}${str}`; | |
} | |
node = node.parentNode; | |
} | |
return str; | |
} | |
} | |
class Solver { | |
constructor() { | |
this.nodeExamining = null; | |
} | |
searchNextNode() { | |
this.playNodeMoveOnGame(this.nodeExamining); | |
if (!window.dead) { | |
this.nodeExamining = this.nodeExamining.getNextNode(); | |
this.searchNextNode(); | |
} | |
} | |
playNodeMoveOnGame(node) { | |
if (node.dir) { | |
this.dispatchMove(node.dir); | |
if (window.dead) { | |
node.score = node.minScore; | |
console.log(node.score + ' - ' + node.getMoveList()); // eslint-disable-line | |
if (!this.bestNode || node.score > this.bestNode.score) { | |
this.bestNode = node; | |
console.log(` *** BEST: ${node.score} ***\n ${node.getMoveList()}\n`); // eslint-disable-line | |
} | |
setTimeout(() => this.restartPlaying(), 0) | |
} | |
} | |
} | |
dispatchMove(dir) { | |
switch (dir) { | |
case 'u': | |
window.dispatchEvent(new KeyboardEvent('keyup', { keyCode: 38 })); | |
break; | |
case 'r': | |
window.dispatchEvent(new KeyboardEvent('keyup', { keyCode: 39 })); | |
break; | |
case 'd': | |
window.dispatchEvent(new KeyboardEvent('keyup', { keyCode: 40 })); | |
break; | |
case 'l': | |
window.dispatchEvent(new KeyboardEvent('keyup', { keyCode: 37 })); | |
break; | |
} | |
} | |
restartPlaying() { | |
this.resetNodeExamining(); | |
if (window.ogMap) { | |
window.MAP = JSON.parse(JSON.stringify(window.ogMap)); | |
window.dead = false; | |
window.myRPG = new RPG(document.getElementById('game').getContext('2d'), window.MAP, document.getElementById('score')); // eslint-disable-line | |
this.searchNextNode(); | |
} else { | |
window.ogMap = JSON.parse(JSON.stringify(window.MAP)); | |
this.createCustomRpg(); | |
document.querySelector('body').innerHTML = `<canvas id="game" width="32" height="32" style="height: 89vh;image-rendering: pixelated;image-rendering: crisp-edges;"> | |
Sorry, this game doesn't work on your browser. Please update it. | |
</canvas><span id="score" style="font-size: 10em;position: relative;top: -1.5em;">0</span>`; | |
this.restartPlaying(); | |
} | |
} | |
resetNodeExamining() { | |
if (this.nodeExamining) { | |
while (this.nodeExamining.parentNode) { | |
this.nodeExamining = this.nodeExamining.parentNode; | |
} | |
} else { | |
this.nodeExamining = new GameTreeNode(); | |
} | |
} | |
// I edited the `RPG` function a bit to remove unnecessary rendering and other | |
// stuff that makes it take longer to play and reset the game. | |
createCustomRpg() { | |
window.RPG = function (ctx, map, score) { | |
var _mapMeanings = { | |
" ": { | |
color: "#000000" | |
}, | |
"E": { | |
color: "#af0000" | |
}, | |
"S": { | |
color: "#ff9800" | |
}, | |
"1": { | |
color: "green" | |
}, | |
"2": { | |
color: "darkgreen" | |
} | |
}; | |
var _enemyLocations = []; | |
var _health = 1; | |
var over = false; | |
var _enemyTick = function (perferAxis) { | |
switch (_turnCount % 12) { | |
case 0: | |
_enemyLocations.push({x: 0, y: 0}); | |
break; | |
case 3: | |
_enemyLocations.push({x: 0, y: 31}); | |
break; | |
case 6: | |
_enemyLocations.push({x: 31, y: 0}); | |
break; | |
case 9: | |
_enemyLocations.push({x: 31, y: 31}); | |
break; | |
} | |
if (!perferAxis) perferAxis = "y"; | |
_enemyLocations.forEach(function (enemy, index) { | |
var xDistance = enemy.x - _player[_currentPlayer].X; | |
var yDistance = enemy.y - _player[_currentPlayer].Y; | |
if (xDistance > 0.5) xDistance = -1; | |
else if (xDistance < -0.5) xDistance = 1; | |
else xDistance = 0; | |
if (yDistance > 0.5) yDistance = -1; | |
else if (yDistance < -0.5) yDistance = 1; | |
else yDistance = 0; | |
if ((xDistance !== 0) && (yDistance !== 0)) { | |
if ((_turnCount % 2) === 0) { | |
xDistance = 0; | |
} else { | |
yDistance = 0; | |
} | |
} | |
var newCoords = _putInBounds(enemy.x + xDistance, enemy.y + yDistance); | |
xDistance = newCoords.x; | |
yDistance = newCoords.y; | |
var conflict = false; | |
enemy.current = true; | |
for (var enemyName in _enemyLocations) { | |
if (_enemyLocations[enemyName].current) continue; | |
if ((_enemyLocations[enemyName].x === xDistance) && (_enemyLocations[enemyName].y === yDistance)) { | |
conflict = true; | |
break; | |
} | |
} | |
enemy.current = false; | |
if (!conflict) { | |
enemy.x = xDistance; | |
enemy.y = yDistance; | |
} | |
if ((enemy.x === _player[1].X) && (enemy.y === _player[1].Y)) { | |
_health--; | |
dead = true; | |
if (onDeath === noop) { | |
over = true; | |
} else { | |
onDeath(); | |
} | |
} | |
_enemyLocations[index] = enemy; | |
}); | |
}; | |
var _putInBounds = function (x, y) { | |
if (x > 31) { | |
x = 31; | |
} | |
if (y > 31) { | |
y = 31; | |
} | |
if (x < 0) { | |
x = 0; | |
} | |
if (y < 0) { | |
y = 0; | |
} | |
return {x: x, y: y}; | |
}; | |
var _player = {}; | |
_player[1] = { | |
X: 16, | |
Y: 16 | |
}; | |
_player[2] = { | |
X: 24, | |
Y: 16 | |
}; | |
this.render = function () { | |
window.scroll(0, 0); | |
score.innerText = _turnCount; | |
map.forEach(function (mapRow, y) { | |
for (var x = 0; x < mapRow.length; x++) { | |
let mapTile = mapRow[x]; | |
if (mapTile === "E") { | |
mapTile = " "; | |
} | |
ctx.fillStyle = _mapMeanings[mapTile].color; | |
ctx.fillRect(x, y, 1, 1); | |
} | |
}); | |
ctx.fillStyle = _mapMeanings["E"].color; | |
_enemyLocations.forEach(function (enemy) { | |
ctx.fillRect(enemy.x, enemy.y, 1, 1); | |
}); | |
ctx.fillStyle = _mapMeanings["1"].color; | |
ctx.fillRect(_player[1].X, _player[1].Y, 1, 1); | |
}; | |
var _turnCount = 0; | |
var _currentPlayer = 1; | |
this.parseKeyPress = function (e) { | |
if (over) return; | |
var perferAxis = "y"; | |
switch (e.keyCode) { | |
case 40: // Down arrow | |
case 83: // S | |
_player[_currentPlayer].Y++; | |
break; | |
case 38: // Up arrow | |
case 87: // W | |
_player[_currentPlayer].Y--; | |
break; | |
case 39: // Right arrow | |
case 68: // D | |
perferAxis = "x"; | |
_player[_currentPlayer].X++; | |
break; | |
case 37: // Left arrow | |
case 65: | |
perferAxis = "x"; | |
_player[_currentPlayer].X--; | |
break; | |
default: | |
return; | |
break; | |
} | |
_turnCount++; | |
var newCoords = _putInBounds(_player[_currentPlayer].X, _player[_currentPlayer].Y); | |
_player[_currentPlayer].X = newCoords.x; | |
_player[_currentPlayer].Y = newCoords.y; | |
_enemyTick(perferAxis); | |
this.render(); | |
}; | |
this.render(); | |
}; | |
} | |
} | |
s = new Solver() | |
s.restartPlaying(); | |
// Run this any time you want to see the best score so far: | |
// console.log(s.bestNode.score) | |
// Run this any time you want to see the move list for the best score so far: | |
// console.log(s.bestNode.getMoveList) | |
// You can do the same to look at the current node being examined: | |
// console.log(s.nodeExamining) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment