Implement bot wrong state engine

master
sigonasr2 4 years ago
parent 93f56a3b27
commit c5b310cdd5
  1. 137
      game.js
  2. 149
      game.test.js

@ -3,29 +3,33 @@ var canvas;
const WAITING = 0; const WAITING = 0;
const RUNNING = 1; const RUNNING = 1;
const REVIEWING = 2; const REVIEWING = 2;
const TESTING = 3;
const FINISH = 4;
const UP = 0; const UP = 0;
const RIGHT = 1; const RIGHT = 1;
const DOWN = 2; const DOWN = 2;
const LEFT = 3; const LEFT = 3;
const RED = 0; const RED = "R";
const BLUE = 1; const BLUE = "B";
const GREEN = 2; const GREEN = "G";
const YELLOW = 3; const YELLOW = "Y";
const PURPLE = 4; const PURPLE = "P";
const PINK = 5; const PINK = "PI";
const BLACK = 6; const BLACK = "BL";
const GRAY = 7; const GRAY = "GR";
const ALIVE = 0; const ALIVE = 0;
const DEAD = 1; const DEAD = 1;
const DONE = 2;
var BOT_X = -1 var BOT_X = -1
var BOT_Y = -1 var BOT_Y = -1
var BOT_DIR = RIGHT var BOT_DIR = RIGHT
var BOT_STATE = ALIVE var BOT_STATE = ALIVE
var BOT_TAPE = [{color:RED},{color:BLUE}] var BOT_TAPE = "RB"
var BOT_QUEUE = []
var BELTDOWN = {type:"BELT",direction:DOWN/*,direction2 - defines a secondary direction. For two belts at once.*/} var BELTDOWN = {type:"BELT",direction:DOWN/*,direction2 - defines a secondary direction. For two belts at once.*/}
var BELTRIGHT = {type:"BELT",direction:RIGHT} var BELTRIGHT = {type:"BELT",direction:RIGHT}
@ -45,6 +49,7 @@ var lastGameUpdate = 0;
var gameSpeed = 1000/1; var gameSpeed = 1000/1;
var gameState=RUNNING; var gameState=RUNNING;
var gameStage=0;
var LEVEL0 = [ var LEVEL0 = [
[{},{},{},{},{},], [{},{},{},{},{},],
@ -76,10 +81,16 @@ var LEVEL4 = [
[{},{...BELTRIGHT,direction2:DOWN},{},{},{},], [{},{...BELTRIGHT,direction2:DOWN},{},{},{},],
[{},{},{...BELTUP,direction2:LEFT},{},{},], [{},{},{...BELTUP,direction2:LEFT},{},{},],
[{},{},{},{},{},],] [{},{},{},{},{},],]
var STAGE1 = {
name:"The First Stage!",
objective:"Accept all bots",
level:createGrid(5,5,4,2),
start:{x:0,y:2},
accept:(tape)=>true}
var gameGrid= [] var gameGrid= []
function createGrid(width,height) { function createGrid(width=5,height=5,exitX=4,exitY=2) {
var grid = [] var grid = []
for (var i=0;i<width;i++) { for (var i=0;i<width;i++) {
var row = [] var row = []
@ -88,9 +99,85 @@ function createGrid(width,height) {
} }
grid.push(row) grid.push(row)
} }
grid[exitY][exitX]={type:"EXIT"}
return grid return grid
} }
function resetGame() {
gameGrid=[]
gameState=WAITING
BOT_X=-1
BOT_Y=-1
BOT_DIR=RIGHT
BOT_STATE=ALIVE
BOT_TAPE="RB"
BOT_QUEUE=[]
lastGameUpdate=0
}
function generateBotQueue() {
if (gameState===TESTING) {
//Iterate up to...15 RED/BLUE combinations.
var MAX_VALUE=1000
var startingValue=0
while (startingValue<MAX_VALUE) {
var tape=ConvertNumberToTape(startingValue++)
//console.log(tape)
var wrongBot=false //Set to true if a bot that's supposed to pass fails, or a bot that's supposed to fail passes.
var isSupposedToBeAccepted=gameStage.accept(tape)
var result=getSimulatedBotResult(tape)
if (result===isSupposedToBeAccepted) {
wrongBot=false;
} else {
wrongBot=true;
}
if (wrongBot) {
BOT_QUEUE.push(tape)
}
if (BOT_QUEUE.length===3) {
break;
}
}
}
}
function getSimulatedBotResult(tape) {
var simulatedBoard = deepCopy(gameGrid)
resetBot(gameStage.start.x,gameStage.start.y,TESTING,tape)
const MAX_ITERATIONS=10000
var iterations=0
while (iterations<MAX_ITERATIONS) {
runBot(true)
//renderGame()
if (gameState===REVIEWING) {
//console.log("Rejected")
return false
}
if (gameState===FINISH) {
//console.log("Accepted")
return true
}
iterations++
}
return false
}
function ConvertNumberToTape(val) {
var remainingVal = val
var tape = ""
while (remainingVal>0) {
var mask = remainingVal&1
if (mask===1) {
tape="B"+tape
} else {
tape="R"+tape
}
remainingVal=remainingVal>>>1
}
return tape;
}
function runBot(testing) { function runBot(testing) {
//console.log(new Date().getTime()) //console.log(new Date().getTime())
if (lastGameUpdate<new Date().getTime()||testing) { if (lastGameUpdate<new Date().getTime()||testing) {
@ -119,17 +206,17 @@ function runBot(testing) {
(nextSquare.direction2===UP||nextSquare.direction2===DOWN))?nextSquare.direction2:nextSquare.direction (nextSquare.direction2===UP||nextSquare.direction2===DOWN))?nextSquare.direction2:nextSquare.direction
}break; }break;
} }
if (nextSquare.direction!==undefined) { if (nextSquare.direction!==undefined||nextSquare.type==="EXIT") {
switch (nextSquare.type) { switch (nextSquare.type) {
case "BRANCH":{ case "BRANCH":{
//console.log("Branch found") //console.log("Branch found")
if (BOT_TAPE[0].color===nextSquare.color1) { if (BOT_TAPE[0]===nextSquare.color1) {
//console.log("Matches color1") //console.log("Matches color1")
//Move towards left side of the branch. //Move towards left side of the branch.
BOT_DIR = LeftOf(nextSquare.direction) BOT_DIR = LeftOf(nextSquare.direction)
ConsumeTape() ConsumeTape()
} else } else
if (BOT_TAPE[0].color===nextSquare.color2) { if (BOT_TAPE[0]===nextSquare.color2) {
//console.log("Matches color2") //console.log("Matches color2")
//Move towards left side of the branch. //Move towards left side of the branch.
BOT_DIR = RightOf(nextSquare.direction) BOT_DIR = RightOf(nextSquare.direction)
@ -144,6 +231,10 @@ function runBot(testing) {
} }
BOT_DIR = nextSquare.direction BOT_DIR = nextSquare.direction
}break; }break;
case "EXIT":{
gameState=FINISH
BOT_STATE=DONE
}break;
} }
//console.log("Direction is now "+BOT_DIR) //console.log("Direction is now "+BOT_DIR)
} else { } else {
@ -154,6 +245,14 @@ function runBot(testing) {
} }
} }
function resetBot(x,y,state,tape) {
gameState=state
BOT_STATE = ALIVE
BOT_DIR = RIGHT
BOT_TAPE=deepCopy(tape)
placeBot(x,y)
}
function placeBot(x,y) { function placeBot(x,y) {
BOT_X = x BOT_X = x
BOT_Y = y BOT_Y = y
@ -173,6 +272,12 @@ function loadLevel(level,botx,boty) {
gameGrid = deepCopy(level) gameGrid = deepCopy(level)
} }
function loadStage(stage) {
//gameGrid=deepCopy(stage.level)
loadLevel(stage.level,stage.start.x,stage.start.y)
gameStage=stage
}
function deepCopy(arr) { function deepCopy(arr) {
var newarr = [] var newarr = []
for (var i=0;i<arr.length;i++) { for (var i=0;i<arr.length;i++) {
@ -209,13 +314,13 @@ function draw() {
} }
function ConsumeTape() { function ConsumeTape() {
BOT_TAPE.shift() BOT_TAPE=BOT_TAPE.substring(1)
} }
function AppendTape(col) { function AppendTape(col) {
BOT_TAPE.push({color:col}) BOT_TAPE+=col
} }
function OverwriteTape(col) { function OverwriteTape(col) {
BOT_TAPE[0]={color:col} BOT_TAPE=col+BOT_TAPE.substring(1)
} }
function LeftOf(dir) { function LeftOf(dir) {

@ -36,18 +36,24 @@ class describe {
this.cb() this.cb()
return this return this
} }
showResults = () =>{
console.log("==============")
console.log("TEST RESULTS: "+TestSuite.passedtests+" passed, "+(TestSuite.totaltests-TestSuite.passedtests)+" failed, "+TestSuite.totaltests+" total")
console.log("==============")
}
} }
function expect(testval1,testval2,test) { function expect(testval1,testval2,name) {
if (testval1!==testval2) { if (testval1!==testval2) {
console.log(" Test Failed!"+" ("+(new Date().getTime()-TestSuite.starttime)+"ms)") console.log(" Test Failed!"+" ("+(new Date().getTime()-TestSuite.starttime)+"ms)"+((name)?` - ${name}`:""))
TestSuite.totaltests++ TestSuite.totaltests++
testsPass=false testsPass=false
} else } else
{ {
TestSuite.totaltests++ TestSuite.totaltests++
TestSuite.passedtests++ TestSuite.passedtests++
console.log(" Test Passed!"+" ("+(new Date().getTime()-TestSuite.starttime)+"ms)") console.log(" Test Passed!"+" ("+(new Date().getTime()-TestSuite.starttime)+"ms)"+((name)?` - ${name}`:""))
} }
} }
@ -67,14 +73,7 @@ function runTests() {
TestSuite = new describe("Bot moving") TestSuite = new describe("Bot moving")
TestSuite TestSuite
.beforeEach(()=>{ .beforeEach(()=>{
gameGrid=[] resetGame()
gameState=WAITING
BOT_X=-1
BOT_Y=-1
BOT_DIR=RIGHT
BOT_STATE=ALIVE
BOT_TAPE=[{color:RED},{color:BLUE}]
lastGameUpdate=0
}) })
.it("Blank level exists.",()=>{ .it("Blank level exists.",()=>{
expect(AllBlankSpaces(LEVEL0),true) expect(AllBlankSpaces(LEVEL0),true)
@ -116,7 +115,7 @@ function runTests() {
.it("Bot obeys branch rules with different colored tape.",()=>{ .it("Bot obeys branch rules with different colored tape.",()=>{
expect(function(){ expect(function(){
loadLevel(LEVEL2,0,2) loadLevel(LEVEL2,0,2)
BOT_TAPE = [{color:BLUE}] BOT_TAPE = "B"
for (var i=0;i<3;i++) {runBot(true)} for (var i=0;i<3;i++) {runBot(true)}
if (BOT_X===2&&BOT_Y===1) { if (BOT_X===2&&BOT_Y===1) {
return true return true
@ -129,7 +128,7 @@ function runTests() {
expect(function(){ expect(function(){
loadLevel(LEVEL2,0,2) loadLevel(LEVEL2,0,2)
for (var i=0;i<3;i++) {runBot(true)} for (var i=0;i<3;i++) {runBot(true)}
if (BOT_TAPE.length===1&&BOT_TAPE[0].color===BLUE) { if (BOT_TAPE.length===1&&BOT_TAPE[0]==="B") {
return true return true
} else { } else {
return false return false
@ -139,7 +138,7 @@ function runTests() {
.it("Bot tape is reduced by 1 when passing through a different branch.",()=>{ .it("Bot tape is reduced by 1 when passing through a different branch.",()=>{
expect(function(){ expect(function(){
loadLevel(LEVEL2,0,2) loadLevel(LEVEL2,0,2)
BOT_TAPE = [{color:BLUE}] BOT_TAPE = "B"
for (var i=0;i<3;i++) {runBot(true)} for (var i=0;i<3;i++) {runBot(true)}
if (BOT_TAPE.length===0) { if (BOT_TAPE.length===0) {
return true return true
@ -163,7 +162,7 @@ function runTests() {
expect(function(){ expect(function(){
loadLevel(LEVEL3,0,2) loadLevel(LEVEL3,0,2)
for (var i=0;i<3;i++) {runBot(true)} for (var i=0;i<3;i++) {runBot(true)}
if (BOT_TAPE.length===3&&BOT_TAPE[2].color===RED) { if (BOT_TAPE.length===3&&BOT_TAPE[2]==="R") {
return true return true
} else { } else {
return false return false
@ -173,9 +172,9 @@ function runTests() {
.it("Bot obeys writer tape rules - Has correct tape when overwriting",()=>{ .it("Bot obeys writer tape rules - Has correct tape when overwriting",()=>{
expect(function(){ expect(function(){
loadLevel(LEVEL3,0,1) loadLevel(LEVEL3,0,1)
BOT_TAPE=[{color:BLUE},{color:RED}] BOT_TAPE="BR"
for (var i=0;i<2;i++) {runBot(true)} for (var i=0;i<2;i++) {runBot(true)}
if (BOT_TAPE.length===2&&BOT_TAPE[0].color===RED) { if (BOT_TAPE.length===2&&BOT_TAPE[0]==="R") {
return true return true
} else { } else {
return false return false
@ -194,6 +193,18 @@ function runTests() {
} }
}(),true) }(),true)
}) })
.it("Bot tape is unaffected if no color matched.",()=>{
expect(function(){
loadLevel(LEVEL2,0,2)
BOT_TAPE = [{color:YELLOW}]
for (var i=0;i<2;i++) {runBot(true)}
if (BOT_TAPE.length===1) {
return true
} else {
return false
}
}(),true)
})
.it("Bot goes right when approaching a double belt (Left->Right) from the left",()=>{ .it("Bot goes right when approaching a double belt (Left->Right) from the left",()=>{
expect(function(){ expect(function(){
loadLevel(LEVEL4,0,2) loadLevel(LEVEL4,0,2)
@ -241,11 +252,109 @@ function runTests() {
} }
}(),true) }(),true)
}) })
.it("Convert Number to Tape function works as expected. 0 bits=R, 1 bits=B",()=>{
expect(ConvertNumberToTape(4),"BRR","4=100=\"BRR\"")
expect(ConvertNumberToTape(24),"BBRRR","24=11000=\"BBRRR\"")
expect(ConvertNumberToTape(167),"BRBRRBBB","167=10100111=\"BRBRRBBB\"")
}).showResults()
console.log("==============") TestSuite = new describe("Stage 1")
console.log("TEST RESULTS: "+TestSuite.passedtests+" passed, "+(TestSuite.totaltests-TestSuite.passedtests)+" failed, "+TestSuite.totaltests+" total") TestSuite
console.log("==============") .beforeEach(()=>{
resetGame()
})
.it("Stage 1 has a level",()=>{
expect(STAGE1.level===undefined,false,"Is defined")
expect(Array.isArray(STAGE1.level),true,"Is an array")
expect(STAGE1.level.length>0,true,"Cannot be empty")
})
.it("Stage 1 has a name",()=>{
expect(STAGE1.name===undefined,false,"Is defined")
expect(typeof(STAGE1.name),"string","Is a string")
expect(STAGE1.name.length>0,true,"Cannot be blank")
})
.it("Stage 1 has an objective",()=>{
expect(STAGE1.objective===undefined,false,"Is defined")
expect(typeof(STAGE1.objective),"string","Is a string")
expect(STAGE1.objective.length>0,true,"Cannot be blank")
})
.it("Stage 1 has a starting position",()=>{
expect(STAGE1.start===undefined,false,"Is defined")
expect(typeof(STAGE1.start),"object","Is an object")
expect(STAGE1.start.x===undefined,false,"Must have an X coordinate")
expect(STAGE1.start.y===undefined,false,"Must have a Y coordinate")
})
.it("Stage 1 has an acceptance condition",()=>{
expect(STAGE1.accept===undefined,false)
expect(typeof(STAGE1.accept),"function")
})
.it("loadStage sets up stage 1",()=>{
loadStage(STAGE1)
expect(gameGrid.length===STAGE1.level.length,true,"Height of stage is equal")
expect(gameGrid[0].length===STAGE1.level[0].length,true,"Width of stage is equal")
})
.it("current stage set to stage 1",()=>{
loadStage(STAGE1)
expect(gameStage===STAGE1,true)
})
.it("accept all bots for stage 1.",()=>{
loadStage(STAGE1)
expect(gameStage.accept(""),true,"Expect true for \"\"")
expect(gameStage.accept("RB"),true,"Expect true for RB")
expect(gameStage.accept("BRBR"),true,"Expect true for BRBR")
})
.it("bot fails at the start.",()=>{
loadStage(STAGE1)
runBot(true)
expect(gameState===REVIEWING,true)
})
.it("When TESTING state is on, the game should test the current level for cases expecting to pass, but fail and create 3 of them if possible.",()=>{
loadStage(STAGE1)
expect(BOT_QUEUE.length===0,true,"Bot queue should be empty.")
generateBotQueue()
expect(BOT_QUEUE.length===0&&gameState!==TESTING,true,"Bot queue should not be modified while state is not TESTING")
gameState=TESTING
generateBotQueue()
expect(BOT_QUEUE.length===3,true,"There should be 3 bots in queue for an unbuilt level, as all bots are supposed to pass.")
})
.it("A stage should have an Exit.",()=>{
loadStage(STAGE1)
var hasAnExit=()=>{
for (var y=0;y<gameGrid.length;y++) {
for (var x=0;x<gameGrid[y].length;x++) {
if (gameGrid[y][x].type==="EXIT") {
return true;
}
}
}
return false;
}
expect(hasAnExit(),true)
})
.it("A bot reaching the exit should set the state to FINISH.",()=>{
loadStage(STAGE1)
placeBot(3,2)
runBot(true)
expect(gameState===FINISH,true)
})
.it("Run a TESTING state to see if an acceptable player-built level has no bots in queue.",()=>{
loadStage(STAGE1)
gameGrid=[
[{},{},{},{},{},],
[{},{},{},{},{},],
[{},{...BELTRIGHT},{...BELTRIGHT},{...BELTRIGHT},{type:"EXIT"},],
[{},{},{},{},{},],
[{},{},{},{},{},],
]
expect(BOT_QUEUE.length===0,true,"Bot queue should be empty.")
gameState=TESTING
generateBotQueue()
//console.log(BOT_QUEUE)
expect(BOT_QUEUE.length===0,true,"There should be 0 bots in queue for a good level, as all bots are supposed to pass.")
})
.showResults()
if (testsPass===undefined) { if (testsPass===undefined) {
testsPass=true testsPass=true
} }

Loading…
Cancel
Save