var canvas;
var currentSound = new Audio();
const GOODQUOTES=[
"Let the conversion begin...",
"All the bots, come to ME",
"Let's convert them ALL",
"What a fantastic machine you have built",
"I like it!",]
const BADQUOTES=[
"I smell a funny one here...",
"I have found a flaw!",
"How could you build me so disfunctionally?",
"???",
"You seemed to miss a step...",]
var TITLESCREENTIMELINE=new Date().getTime()
const TITLETIMELINE = [
{time:0,cb:
(ctx)=>{
SCENE_DRAW=(ctx)=>{
ctx.globalAlpha=Math.min((new Date().getTime()-TITLESCREENTIMELINE)/3000,1)
ctx.drawImage(IMAGE_SIG,canvas.width/2-243,canvas.height/2-22)
}
}
},
{time:4000,cb:
(ctx)=>{
SCENE_DRAW=(ctx)=>{
ctx.globalAlpha=Math.max(
(7000-(new Date().getTime()-TITLESCREENTIMELINE))/3000
,0)
ctx.drawImage(IMAGE_SIG,canvas.width/2-243,canvas.height/2-22)
}
}
},
{time:4000,cb:
(ctx)=>{
currentSound.src="Super 8 Old Movie Projector - Gaming Sound Effect.mp3"
currentSound.play()
}
},
{time:7000,cb:
(ctx)=>{
SCENE_DRAW=(ctx)=>{
SCENEALPHA=0
if (Math.random()<=0.02) {
ctx.globalAlpha=0.5
ctx.drawImage(IMAGE_TITLE,canvas.width/2+Math.random()*640260,canvas.height/2+Math.random()*64-70)
} else {
ctx.globalAlpha=0.75+Math.random()*0.25
ctx.drawImage(IMAGE_TITLE,canvas.width/2-260,canvas.height/2-70)
}
}
}
},
{time:15000,cb:
(ctx)=>{
currentSound.src="Shostakovich_ Symphony No. 9.mp3"
currentSound.loop=true
currentSound.play()
SCENE_DRAW=(ctx)=>{
ctx.globalAlpha=0.9
ctx.drawImage(IMAGE_TITLE,canvas.width/2-260,canvas.height/2-70)
ctx.font="bold 48px 'Zilla Slab', serif"
ctx.fillStyle="black"
ctx.strokeStyle="white"
ctx.textAlign = "center"
ctx.fillText("- Click to Play -",canvas.width/2,canvas.height*0.9)
ctx.strokeText("- Click to Play -",canvas.width/2,canvas.height*0.9)
}
}
},
]
var SCENEBACKGROUND = "black"
var SCENEALPHA = 1.0
var CURRENTTIMELINE = []
var SCENE_DRAW = ()=>{}
var FIRSTBOOT = true
const WAITING = 0;
const RUNNING = 1;
const REVIEWING = 2;
const TESTING = 3;
const FINISH = 4;
const PAUSED = 5;
const MAINMENU = 6;
const TITLE = 7;
const STARTUP = 8;
const INFO = 9;
var WAITINGTOFINISH=false
const ONE_TEST = -1;
const BLANK_TEST = -2;
const NORMAL_TEST = 0;
const EVEN_LENGTH_TEST = 1; //Only generate even length tapes.
const ODD_LENGTH_TEST = 3; //Only generates odd length tapes.
const BINARY_TEST = 4; //Generates valid binary numbers (never has a trailing R)
const BSTAGE2_TEST = 2;
const TWO_NUMBERS_TEST = 5; //Generates two binary numbers separated by a green dot.
const SUBSTRING_TEST = 6; //Generates two yellow dots to indicate a substring to cut out.
const UPPERCASE_CHARACTER_TEST = 7; //Generates uppercase letters separated by yellow.
const TWO_UNEQUAL_NUMBERS_TEST = 8; //Two unequal numbers will be generated separated by a green.
const NONE = 0;
const BINARY = 1;
const STRING = 2;
var ISTESTING = false;
var MOUSEOVERTIME = -1
const UP = 0;
const RIGHT = 1;
const DOWN = 2;
const LEFT = 3;
const RED = "R";
const BLUE = "B";
const GREEN = "G";
const YELLOW = "Y";
const PURPLE = "P";
const PINK = "p";
const BLACK = "b";
const GRAY = "g";
const ALIVE = 0;
const DEAD = 1;
const DONE = 2;
var BOT_X = -1
var BOT_Y = -1
var BOT_DIR = RIGHT
var BOT_STATE = ALIVE
var BOT_TAPE = "RB"
var GHOST_BOT_TAPE = "RB"
var BOT_START_TAPE = BOT_TAPE
var BOT_QUEUE = []
var DELETEMODE = false
var DRAGGING = false
var MOUSEDOWN = false
var LAST_MOUSE_X=0;
var LAST_MOUSE_Y=0;
var DRAG_X = -1
var DRAG_Y = -1
var BOT_DID_NOT_REACH_EXIT = false
var TOOLTIPDISPLAYED = undefined
var MOVEMODE = false
var STARTDRAG = undefined
var TEST_RANDOM_TAPE = ""
var MESSAGETIMER = -1
var EXPECTED = true //True means the bot was supposed to accepted, false means the bot was supposed to be rejected.
var RESULT = true //True means you won. False means you lost.
var TESTSTEPS = 0 //How long it takes the tests to run.
var BOT_PREVX = BOT_X
var BOT_PREVY = BOT_Y
var LASTPOSITIONUPDATE = 0
var BRIDGEDBELT = false
var ALLOWEDTOCLICK = true
var MOBILE = false
var BELTDOWN = {type:"BELT",direction:DOWN/*,direction2 - defines a secondary direction. For two belts at once.*/}
var BELTRIGHT = {type:"BELT",direction:RIGHT}
var BELTUP = {type:"BELT",direction:UP}
var BELTLEFT = {type:"BELT",direction:LEFT}
var BRANCHDOWN = {type:"BRANCH",direction:DOWN,color1:RED,color2:BLUE} //color 1 points clockwise(right), color 2 points counter-clockwise(left)
var BRANCHLEFT = {type:"BRANCH",direction:LEFT,color1:RED,color2:BLUE}
var BRANCHRIGHT = {type:"BRANCH",direction:RIGHT,color1:RED,color2:BLUE}
var BRANCHUP = {type:"BRANCH",direction:UP,color1:RED,color2:BLUE}
var WRITERDOWN = {type:"WRITER",direction:DOWN,color1:RED/*overwrite - if turned on, the writer overwrites the current tape position instead of appending.*/}
var WRITERLEFT = {type:"WRITER",direction:LEFT,color1:RED}
var WRITERRIGHT = {type:"WRITER",direction:RIGHT,color1:RED}
var WRITERUP = {type:"WRITER",direction:UP,color1:RED}
var DEF_BRANCHUP_RB = {img:ID_BRANCH,color1:RED,color2:BLUE,type:"BRANCH"}
var DEF_BRANCHUP_BR = {img:ID_BRANCH,color1:BLUE,color2:RED,type:"BRANCH"}
var DEF_BRANCHUP_GY = {img:ID_BRANCH,color1:GREEN,color2:YELLOW,type:"BRANCH"}
var DEF_BRANCHUP_YG = {img:ID_BRANCH,color1:YELLOW,color2:GREEN,type:"BRANCH"}
var DEF_BRANCHUP_PPI = {img:ID_BRANCH,color1:PURPLE,color2:PINK,type:"BRANCH"}
var DEF_BRANCHUP_PIP = {img:ID_BRANCH,color1:PINK,color2:PURPLE,type:"BRANCH"}
var DEF_BRANCHUP_BLGR = {img:ID_BRANCH,color1:BLACK,color2:GRAY,type:"BRANCH"}
var DEF_BRANCHUP_GRBL = {img:ID_BRANCH,color1:GRAY,color2:BLACK,type:"BRANCH"}
var DEF_WRITERRIGHT_R = {img:ID_WRITER,color1:RED,type:"WRITER"}
var DEF_WRITERRIGHT_B = {img:ID_WRITER,color1:BLUE,type:"WRITER"}
var DEF_WRITERRIGHT_G = {img:ID_WRITER,color1:GREEN,type:"WRITER"}
var DEF_WRITERRIGHT_Y = {img:ID_WRITER,color1:YELLOW,type:"WRITER"}
var DEF_WRITERRIGHT_P = {img:ID_WRITER,color1:PURPLE,type:"WRITER"}
var DEF_WRITERRIGHT_PI = {img:ID_WRITER,color1:PINK,type:"WRITER"}
var DEF_WRITERRIGHT_BL = {img:ID_WRITER,color1:BLACK,type:"WRITER"}
var DEF_WRITERRIGHT_GR = {img:ID_WRITER,color1:GRAY,type:"WRITER"}
var DEF_CONVEYOR = {img:ID_CONVEYOR,type:"BELT"}
var GRID_W = 32
var GRID_H = 32
var GRID_X = 20
var GRID_Y = 20
var ITEM_DIRECTION=RIGHT;
var dashOffset=0
var SUBMENU = {
visible:false,
width:0,
height:0,
buttons:[]
}
var BUTTON_SELECTED = undefined
var ITEM_SELECTED = undefined
var KEY_ROTATION_RIGHT = ["D","L","d","l","6","ArrowRight"]
var KEY_ROTATION_LEFT = ["A","G","a","h","4","ArrowLeft"]
var KEY_ROTATION_UP = ["W","K","w","k","8","ArrowUp"]
var KEY_ROTATION_DOWN = ["S","J","s","j","2","ArrowDown"]
var KEY_BRIDGED_BELT = ["Shift"]
var CONVEYOR_BUILD_BUTTON = {img:ID_CONVEYOR,x:-1,y:-1,w:-1,h:-1,lastselected:DEF_CONVEYOR,tooltip:"Conveyor Belt\nMoves bots in a direction.\n\nHold (Shift) to bridge over other belts",mobileTooltip:"Conveyor Belt\nMoves bots in a direction.\n\nHold button down to toggle bridge mode.\nUsed to bridge over other belts"}
var BRANCH_BUILD_BUTTON = {img:ID_BRANCH,x:-1,y:-1,w:-1,h:-1,submenu_buttons:[DEF_BRANCHUP_RB,DEF_BRANCHUP_BR,DEF_BRANCHUP_GY,DEF_BRANCHUP_YG,DEF_BRANCHUP_PPI,DEF_BRANCHUP_PIP,DEF_BRANCHUP_BLGR,DEF_BRANCHUP_GRBL],lastselected:undefined,default:DEF_BRANCHUP_RB,tooltip:"Branch\nReads next tape and moves bot in the\nmatching color direction.\n\nMoves bot forward and does not consume\ntape if no match found."}
var WRITER_BUILD_BUTTON = {img:ID_WRITER,x:-1,y:-1,w:-1,h:-1,submenu_buttons:[DEF_WRITERRIGHT_R,DEF_WRITERRIGHT_B,DEF_WRITERRIGHT_G,DEF_WRITERRIGHT_Y,DEF_WRITERRIGHT_P,DEF_WRITERRIGHT_PI,DEF_WRITERRIGHT_BL,DEF_WRITERRIGHT_GR],lastselected:undefined,default:DEF_WRITERRIGHT_R,tooltip:"Writer\nWrites a color to the end of the tape."}
var ROTATE_CLOCKWISE_BUTTON = {img:ID_ROTATE_CLOCKWISE,x:-1,y:-1,w:-1,h:-1,cb:rotateClockwise,tooltip:"Rotate selection clockwise.\nYou can use WASD, HJKL, Arrow Keys\nto rotate quickly.",mobileTooltip:"Rotate selection clockwise."
}
var ROTATE_COUNTERCLOCKWISE_BUTTON = {img:ID_ROTATE_COUNTERCLOCKWISE,x:-1,y:-1,w:-1,h:-1,cb:rotateCounterClockwise,tooltip:"Rotate selection counter-clockwise.\nYou can use WASD, HJKL, Arrow Keys\nto rotate quickly.",mobileTooltip:"Rotate selection counter-clockwise."
}
var PLAY_BUTTON = {img:ID_PLAY,x:-1,y:-1,w:-1,h:-1,cb:runGameSimulation,tooltip:"Test your great machine.\n\nConvert all the bots!"
}
var PAUSE_BUTTON = {img:ID_PAUSE,x:-1,y:-1,w:-1,h:-1,cb:pauseGameSimulation,tooltip:"Pause the great machine."
}
var RESET_BUTTON = {img:ID_RESET,x:-1,y:-1,w:-1,h:-1,cb:resetSimulation,tooltip:"Reset the great machine."
}
var DELETE_BUTTON = {img:ID_DELETE,x:-1,y:-1,w:-1,h:-1,cb:toggleDeleteMode,tooltip:"Remove a piece on the field."
}
var HOME_BUTTON = {img:ID_HOME,x:-1,y:-1,w:-1,h:-1,cb:goHome,tooltip:"Go back to the level selection menu.\n\nYour progress will be saved automatically."
}
var MOVE_BUTTON = {img:ID_MOVE_BUTTON,x:2,y:2,w:32,h:32,cb:toggleMoveMode,tooltip:"Scroll the view area."
}
var AUDIO_BUTTON = {
bounds:{x:4,y:4,w:40,h:40},
cb:()=>{
currentSound.muted=!currentSound.muted
}
}
var INFO_BUTTON = {
bounds:{x:4,y:44,w:40,h:40},
cb:()=>{
gameState=INFO
}
}
var INFO_HOME_BUTTON = {
bounds:{x:4,y:4,w:40,h:40},
cb:()=>{
goHome(false)
}
}
var CREDITS=">CREDITS\n\n"
+"First, thanks to Javidx9 for putting on a wonderful coding jam!\n"
+"I had a lot of fun working on this game.\n"
+"\n"
+"This game is heavily inspired by Manufactoria, and thus\n"
+"it is appropriate to give credit to the developer of Manufactoria\n"
+">(@pleasingfungus) Nicholas Feinberg - Creator of Manufactoria\n"
+"\n"
+">Music is from an excerpt from the Shostakovich - Symphony No.9 Performance\n"
+"\n"
+"All graphics were designed by myself.\n"
+">(@sigonasr2) Joshua Sigona - Creator of The Great Conversion\n"
+"\n"
+"Font licenses are included in attached License files."
var MENU = {
visible:false,
buttons:[CONVEYOR_BUILD_BUTTON,BRANCH_BUILD_BUTTON,WRITER_BUILD_BUTTON,ROTATE_COUNTERCLOCKWISE_BUTTON,ROTATE_CLOCKWISE_BUTTON,DELETE_BUTTON,PLAY_BUTTON,RESET_BUTTON,HOME_BUTTON]
}
function toggleMoveMode() {
setMoveMode(!MOVEMODE)
}
function saveLevelData() {
completedStages[gameStage.name].data=deepCopy(gameGrid)
localStorage.setItem("game",JSON.stringify(completedStages))
}
function goHome(save=true) {
if (save) {
saveLevelData()
}
MENU.visible=false
BRIDGEDBELT=false
setMoveMode(false)
BOT_TAPE=""
GHOST_BOT_TAPE=""
GRID_X=20
GRID_Y=20
ITEM_SELECTED=undefined
MOUSEDOWN=false
ALLOWEDTOCLICK=false
endARound()
for (var button of MENU.buttons) {
if (button.submenu_buttons) {
button.lastselected=button.default
}
}
gameGrid=[]
gameState=MAINMENU
}
function runGameSimulation(){
if (gameState!==PAUSED) {
gameState=TESTING
ISTESTING=true
BOT_PREVX=-100
BOT_PREVY=-100
BOT_TAPE=""
GHOST_BOT_TAPE=""
ITEM_SELECTED=undefined
BOT_START_TAPE=undefined
if (gameSpeed===-1) {
gameSpeed=1000/1
}
setMoveMode(false)
generateBotQueue()
ISTESTING=false
if (BOT_QUEUE.length>0) {
BOT_TAPE=BOT_QUEUE[0]
} else {
BOT_TAPE=TEST_RANDOM_TAPE
EXPECTED=gameStage.accept(BOT_TAPE)
}
BOT_START_TAPE=BOT_TAPE
BOT_X=gameStage.start.x
BOT_Y=gameStage.start.y
BOT_PREVX=BOT_X
BOT_PREVY=BOT_Y
BOT_STATE=ALIVE
gameState=WAITING
BOT_DIR=RIGHT
gameState=RUNNING
gameState=RUNNING
MESSAGETIMER=new Date().getTime()+3000
for (var i=0;i
true}
var STAGE2 = {
name:"Blue Blue",
objective:"Accept only bots that have all B markers.",
level:createGrid(5,5,4,2),
start:{x:0,y:2},
accept:(tape)=>{
if (tape.length===0) {
return false;
}
for (var i=0;i{
var reds=0;
var blues=0;
for (var i=0;i{
var finalTape=""
for (var i=0;i{
var finalTape=""
for (var i=0;i{
var finalTape=""
for (var i=0;i{
var finalTape = tape.substring(0,tape.length-1)
finalTape+=YELLOW
return finalTape
},
//generator:BLANK_TEST,
//display:STRING
}
var BSTAGE5 = {
name:"Back and Forth",
objective:`Accept robots that have alternating colors.`,
level:createGrid(7,7,6,6),
start:{x:0,y:0},
accept:(tape)=>{
var finalTape=""
var lastCol=""
for (var i=0;i{
return {string:"#olcCodeJam2020"}
},
generator:BLANK_TEST,
display:STRING
}
var BSTAGE7 = {
name:"End-to-End",
objective:`Accept robots that have the same first and last colors on their tapes. Ex: ${RED}${BLUE}${RED} is accepted, ${RED}${BLUE}${BLUE} is not.`,
level:createGrid(7,7,6,6),
start:{x:0,y:0},
accept:(tape)=>{
var first=""
var last=""
if (tape.length>0) {
first=tape[0]
last=tape[tape.length-1]
}
return first===last
},
//generator:BLANK_TEST,
//display:STRING
}
var BSTAGE8 = {
name:"Greatest Count",
objective:`Given an odd number of ${BLUE}s and ${RED}s, determine if there are more ${BLUE}s or more ${RED}s and only return those markers. Ex: ${BLUE}${BLUE}${BLUE}${RED}${RED} should output as ${BLUE}${BLUE}${BLUE} (3>2)`,
level:createGrid(9,9,8,8),
start:{x:0,y:0},
accept:(tape)=>{
var blues = ""
var reds = ""
for (var i=0;ireds.length)?blues:reds
},
generator:ODD_LENGTH_TEST,
//display:STRING
}
var ISTAGE2 = {
name:"Oddly Even",
objective:`Accepts bots that have an even length of tape.`,
level:createGrid(7,7,6,6),
start:{x:0,y:0},
accept:(tape)=>{
return tape.length%2===0
},
//generator:BLANK_TEST,
//display:STRING
}
var ISTAGE3 = {
name:"Add One",
objective:`Given a tape representing binary (${RED}=0 ${BLUE}=1), add 1 to the number. Ex: ${BLUE}${RED}${BLUE}(5) should output ${BLUE}${BLUE}${RED}(6)`,
level:createGrid(9,9,8,8),
start:{x:0,y:0},
accept:(tape)=>{
var answer = tapeToBinary(tape)+1
return binaryToTape(answer)
},
generator:BINARY_TEST,
display:BINARY
}
var ISTAGE4 = {
name:"String Length",
objective:`Return the number of ${RED}s from a bot as a binary number (${RED}=0 ${BLUE}=1). Ex: ${RED}${RED}${RED}${BLUE}${RED} should output as ${BLUE}${RED}${RED} (4 ${RED}s)`,
level:createGrid(13,13,12,12),
start:{x:0,y:0},
accept:(tape)=>{
var reds = 0
for (var i=0;i{
var blues = ""
var reds = ""
for (var i=0;i{
for (var i=0;i{
var temp = ""
var number1 = -1
var number2 = -1
for (var i=0;i{
var found = ""
var start = false
for (var i=0;i ${BLUE}${BLUE}${RED}${BLUE}${BLUE}${RED}${RED}${RED}`,
level:createGrid(11,11,10,10),
start:{x:0,y:0},
accept:(tape)=>{
return tape.split("").reverse().join("")
},
//generator:SUBSTRING_TEST,
//display:BINARY
}
var ASTAGE1 = {
name:"Right in the Center",
objective:`Given an even length tape, add a ${YELLOW} marker in the middle of it.`,
level:createGrid(10,10,9,9),
start:{x:0,y:0},
accept:(tape)=>{
return tape.substring(0,tape.length/2)+"Y"+tape.substring(tape.length/2,tape.length)
},
generator:EVEN_LENGTH_TEST,
//display:STRING
}
var ASTAGE2 = {
name:"toLowerCase",
objective:`Given a string of uppercase ASCII binary characters (${RED}=0 ${BLUE}=1) separated by ${YELLOW}, convert all uppercase to lowercase. Ex: "HI"= ${BLUE}${RED}${RED}${BLUE}${RED}${RED}${RED}Y${BLUE}${RED}${RED}${BLUE}${RED}${RED}${BLUE} output as "hi"= ${BLUE}${BLUE}${RED}${BLUE}${RED}${RED}${RED}Y${BLUE}${BLUE}${RED}${BLUE}${RED}${RED}${BLUE}`,
level:createGrid(25,25,24,24),
start:{x:0,y:0},
accept:(tape)=>{
var chars=tape.split(YELLOW)
var finalString = ""
for (var i=0;i{
var temp = ""
var number1 = -1
var number2 = -1
for (var i=0;i ${RED}${BLUE}${GREEN}${BLUE}${BLUE}${RED}${BLUE}${BLUE}${BLUE}${BLUE}${GREEN} -> ${RED}${BLUE}${GREEN}${BLUE}${RED}${RED}${BLUE}${BLUE}${BLUE}${BLUE}${GREEN} -> ${RED}${BLUE}${GREEN}${BLUE}${BLUE}${GREEN}${RED}${BLUE}${BLUE}${BLUE}${BLUE}${GREEN}`,
level:createGrid(13,13,6,6),
start:{x:0,y:6},
accept:(tape)=>{
var first=false;
var newString=""
var found=true
var limit=2
while (found&&limit-->0) {
found=false
first=false
//Red->Blue-Green
for (var i=0;i RR
var last = ""
for (var i=0;i24)`,
level:createGrid(17,17,16,16),
start:{x:0,y:0},
accept:(tape)=>{
var temp = ""
var number1 = -1
var number2 = -1
for (var i=0;inumber2)?number1:number2)
},
generator:TWO_UNEQUAL_NUMBERS_TEST,
display:BINARY
}
var TUTORIAL1 = {
name:"Conveyors!",
objective:"To convert your robots, you must send them from the entrance to the exit. Select a belt and send robots to where they truly belong. Press the Play button to test your machine!",
level:createGrid(5,5,4,2),
start:{x:0,y:2},
locked:[BRANCH_BUILD_BUTTON,WRITER_BUILD_BUTTON],
tutorial:true,
accept:(tape)=>{
return true;
},
//display:BINARY
}
var TUTORIAL2 = {
name:"Branches",
objective:"We have to make sure we are sending robots that meet our needs! Use the branch to filter out robots that start with a R signal. Send only those to the exit!",
level:createGrid(5,5,4,2),
start:{x:0,y:2},
locked:[WRITER_BUILD_BUTTON],
tutorial:true,
accept:(tape)=>{
if (tape[0]===RED) {
return true;
} else {
return false;
}
},
//generator:BLANK_TEST,
//display:STRING
}
var TUTORIAL3 = {
name:"Writers",
objective:"CONVERSION! It's time to convert robots to what they should be. Add 3 B signals to every bot that comes through. We shall convert them all!",
level:createGrid(5,5,4,2),
start:{x:0,y:2},
tutorial:true,
accept:(tape)=>{
return tape+BLUE+BLUE+BLUE
}
}
var TUTORIAL4 = {
name:"More Colors",
objective:"You may be required to use different colors, either for your purposes or mine. For this robot cycle, convert all B and R signals to Y signals.",
level:createGrid(5,5,4,2),
start:{x:0,y:2},
accept:(tape)=>{
var newTape = ""
for (var i=0;i{
return true
}
}
var TUTORIALMENU={
title:"Introduction to Conversion",
levels:[TUTORIAL1,TUTORIAL2,TUTORIAL3,TUTORIAL4],
cols:2,
width:568*0.8
}
var EASYMENU={
title:"Beginner Stages",
levels:[BSTAGE1,BSTAGE2,BSTAGE3,BSTAGE4,BSTAGE5,BSTAGE6,BSTAGE7,BSTAGE8],
cols:1,
width:568*0.33
}
var MEDIUMMENU={
title:"Intermediate Stages",
levels:[ISTAGE1,ISTAGE2,ISTAGE3,ISTAGE4,ISTAGE5,ISTAGE6,ISTAGE7,ISTAGE8],
cols:1,
width:568*0.33
}
var HARDMENU={
title:"Advanced Stages",
levels:[ASTAGE1,ASTAGE2,ASTAGE3,ASTAGE4,ASTAGE5],
cols:1,
width:568*0.33
}
var SANDBOXMENU={
title:"Sandbox",
levels:[SANDBOX],
cols:1,
width:568*0.33
}
var gameGrid = []
var completedStages = undefined //Example completed structure.
function setCookie(cname, cvalue, exdays) {
var d = new Date();
d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
var expires = "expires="+d.toUTCString();
document.cookie = cname + "=" + cvalue + ";" + expires + ";";
}
function getCookie(cname) {
var name = cname + "=";
var ca = document.cookie.split(';');
for(var i = 0; i < ca.length; i++) {
var c = ca[i];
while (c.charAt(0) == ' ') {
c = c.substring(1);
}
if (c.indexOf(name) == 0) {
return c.substring(name.length, c.length);
}
}
return "";
}
function createGrid(width=5,height=5,exitX=4,exitY=2) {
var grid = []
for (var i=0;i0) {
var mask = numb&7
switch (mask) {
case 0:{
newTape+=GREEN
}break;
case 1:{
newTape+=YELLOW
}break;
case 2:{
newTape+=PURPLE
}break;
case 3:{
newTape+=PINK
}break;
case 4:{
newTape+=BLACK
}break;
case 5:{
newTape+=GRAY
}break;
default:{
newTape+=GREEN
}
}
numb=numb>>>3
}
tests.push(newTape)
}
}break;
case EVEN_LENGTH_TEST:{
while (tests.length<2000) {
var newTape = binaryToTape(startingValue++)
if (newTape.length%2===0) {
tests.push(newTape)
tests.push(newTape.split("").reverse().join(""))
}
}
}break;
case ODD_LENGTH_TEST:{
while (tests.length<2000) {
var newTape = binaryToTape(startingValue++)
if (newTape.length%2===1) {
tests.push(newTape)
tests.push(newTape.split("").reverse().join(""))
}
}
}break;
case BINARY_TEST:{
while (tests.length<2000) {
var newTape = binaryToTape(startingValue++)
if (newTape[newTape.length-1]!==RED) {
tests.push(newTape)
}
}
}break;
case TWO_NUMBERS_TEST:{
while (tests.length<2000) {
var newTape = binaryToTape(startingValue++)
var newTape2 = binaryToTape(startingValue++)
if (Math.random()<=0.5) {
newTape2=newTape
}
if (newTape[newTape.length-1]!==RED&&newTape2[newTape2.length-1]!==RED) {
tests.push(newTape+GREEN+newTape2)
}
}
}break;
case SUBSTRING_TEST:{
startingValue=1000
while (tests.length<2000) {
var tape = binaryToTape(startingValue)
var pos1 = Math.floor(Math.random()*(tape.length/2))
var pos2 = (tape.length/2)+Math.floor(Math.random()*(tape.length/2))
tests.push(tape.substring(0,pos1)+YELLOW+tape.substring(pos1,pos2)+YELLOW+tape.substring(pos2,tape.length-1))
startingValue+=141
}
}break;
case UPPERCASE_CHARACTER_TEST:{
while (tests.length<2000) {
var length=Math.floor(Math.random()*15)
var word = ""
for (var i=0;i=3) {
break;
}
}
if (RESULT) {
completedStages[gameStage.name].complete=true
completedStages[gameStage.name].score=TESTSTEPS
}
}
}
function getSimulatedBotResult(tape) {
var simulatedBoard = deepCopy(gameGrid)
resetBot(gameStage.start.x,gameStage.start.y,TESTING,tape)
const MAX_ITERATIONS=10000
var iterations=0
while (iterations0) {
var mask = remainingVal&1
if (mask===1) {
tape=tape+BLUE
} else {
tape=tape+RED
}
remainingVal=remainingVal>>>1
}
return tape;
}
function setNextSquare(offsetX,offsetY) {
if (gameGrid[BOT_Y+offsetY]!==undefined) {
var nextSquare = gameGrid[BOT_Y+offsetY][BOT_X+offsetX];
if (!ISTESTING) {
BOT_PREVX=BOT_X
BOT_PREVY=BOT_Y
}
LASTPOSITIONUPDATE=new Date().getTime()
BOT_X+=offsetX
BOT_Y+=offsetY
return nextSquare
} else {
gameState = REVIEWING
BOT_STATE = DEAD
endARound()
return undefined
}
}
function runBot(testing) {
if ((lastGameUpdate=button.x &&
getMousePos(e).x<=button.x+button.w &&
getMousePos(e).y>=button.y &&
getMousePos(e).y<=button.y+button.h)
}
function setMoveMode(mode) {
MOVEMODE=mode
if (MOVEMODE) {
document.body.style.cursor="move"
} else {
if (DELETEMODE) {
document.body.style.cursor="url('delete_cursor.png') 8 8,auto"
} else {
document.body.style.cursor="url('cursor.png') 8 8,auto"
}
}
}
function clickEvent(e) {
if (window.TouchEvent && e instanceof TouchEvent) {
MOBILE=true
e.preventDefault()
} else {
MOBILE=false
}
var mousepos = getMousePos(e)
LAST_MOUSE_X=mousepos.x
LAST_MOUSE_Y=mousepos.y
MOUSEOVERTIME=-1
MOUSEDOWN=true
if (gameState===STARTUP) {
currentSound.play()
setupTitleScreen()
MOUSEDOWN=false
}
if (gameState===TITLE) {
MOUSEDOWN=false
if (new Date().getTime()-TITLESCREENTIMELINE>=15000) {
if (FIRSTBOOT) {
loadStage(TUTORIAL1)
gameState=WAITING
} else {
gameState=MAINMENU
}
}
}
if (gameState===MAINMENU) {
if (MouseOverBounds(AUDIO_BUTTON.bounds)) {AUDIO_BUTTON.cb();return;}
if (MouseOverBounds(INFO_BUTTON.bounds)) {INFO_BUTTON.cb();return;}
}
if (gameState===INFO) {
if (MouseOverBounds(INFO_HOME_BUTTON.bounds)) {INFO_HOME_BUTTON.cb();return;}
}
if (MENU.visible) {
for (var button of MENU.buttons) {
if (ButtonIsUnlocked(button)) {
if (mouseOverButton(canvas,e,button)) {
if (MOBILE&&button===CONVEYOR_BUILD_BUTTON) {
setTimeout(()=>{
if (MOUSEDOWN&&
LAST_MOUSE_X>=CONVEYOR_BUILD_BUTTON.x&&
LAST_MOUSE_X<=CONVEYOR_BUILD_BUTTON.x+CONVEYOR_BUILD_BUTTON.w&&
LAST_MOUSE_Y>=CONVEYOR_BUILD_BUTTON.y&&
LAST_MOUSE_Y<=CONVEYOR_BUILD_BUTTON.y+CONVEYOR_BUILD_BUTTON.h) {
BRIDGEDBELT=!BRIDGEDBELT
}
},500)
}
if (button.cb!==undefined) {
button.cb()
return;
} else {
DELETEMODE=false
document.body.style.cursor="url('cursor.png') 8 8,auto"
if (button.submenu_buttons&&button.submenu_buttons.length>0) {
BUTTON_SELECTED=button;
SUBMENU.visible=true;
SUBMENU.buttons=[]
var index = 0;
for (var button2 of BUTTON_SELECTED.submenu_buttons) {
var buttonX = ((index%3)*48)+16
var buttonY = canvas.height*0.8-(Math.floor(index/3)*48)-40
SUBMENU.buttons.push({def:button2,img:button2.img,x:buttonX,y:buttonY,w:32,h:32})
index++;
}
}
if (ITEM_SELECTED===button.lastselected&&!MOVEMODE) {
if (!MOBILE) {
ITEM_SELECTED=undefined
}
} else {
ITEM_SELECTED=button.lastselected
}
setMoveMode(false)
return
}
}
}
}
if (mouseOverButton(canvas,e,MOVE_BUTTON)&&!DELETEMODE) {
MOVE_BUTTON.cb()
return
}
}
if (gridModeIsAvailable()&&ITEM_SELECTED===undefined) {
setMoveMode(true)
}
if (!MOBILE&&e.button!==0) {
e.preventDefault()
if (gridModeIsAvailable()) {
setMoveMode(true)
} else {
setMoveMode(false)
}
}
if (MOVEMODE) {
//Cannot handle building until out of move mode.
STARTDRAG=getMousePos(e)
return;
}
if ((ITEM_SELECTED!==undefined||DELETEMODE)&&!MOVEMODE) {
var clickCoords = getGridCoords(getMousePos(e))
if (coordsInBounds(clickCoords)) {
modifyBoard(clickCoords,ITEM_SELECTED)
DRAGGING = true
DRAG_X = clickCoords.x
DRAG_Y = clickCoords.y
}
}
}
function coordsInBounds(coords) {
return coords.x>=0&&coords.y>=0&&coords.y0) {
var currentEvent = CURRENTTIMELINE[0]
if (currentEvent.time") {
string=split[i].substring(1)
ctx.font="bold 16px 'Zilla Slab', serif"
} else {
ctx.font="16px 'Zilla Slab', serif"
}
ctx.textAlign = "center"
ctx.fillStyle="black"
ctx.fillText(string,canvas.width/2,i*20+36)
}
}break;
case STARTUP:{
ctx.fillStyle="black"
ctx.fillRect(0,0,canvas.width,canvas.height)
ctx.font="16px 'Zilla Slab', serif"
ctx.fillStyle="white"
ctx.textAlign = "left"
ctx.fillText("Booting...",0,24)
ctx.fillText("Click to begin",0,48)
}break;
default:{
renderGame(ctx)
if (ITEM_SELECTED&&!MOBILE) {
RenderIcon(LAST_MOUSE_X-16,LAST_MOUSE_Y-16,ctx,ITEM_SELECTED,ITEM_DIRECTION)
}
//drawImage(0,0,ID_CONVEYOR,ctx,0)
//drawImage(LAST_MOUSE_X,LAST_MOUSE_Y,ID_ARROW,ctx,0)
RenderSubmenu(ctx)
RenderMenu(ctx)
RenderGameInfo(ctx)
}
}
}
}
function RenderSpeedbar(x,y,w,ctx) {
var gradient = ctx.createLinearGradient(x, 32, x+w, 32);
gradient.addColorStop(0,"rgb(105, 55, 51)")
gradient.addColorStop(1,"rgb(54, 77, 255)")
ctx.lineWidth = 6;
ctx.lineCap = "round"
ctx.strokeStyle="white"
ctx.setLineDash([])
ctx.beginPath()
ctx.moveTo(x,y)
ctx.lineTo(x+w,y)
ctx.stroke();
ctx.lineWidth = 4;
ctx.lineCap = "round"
ctx.strokeStyle=gradient
ctx.setLineDash([])
ctx.beginPath()
ctx.moveTo(x,y)
ctx.lineTo(x+w,y)
ctx.stroke();
ctx.lineWidth = 2;
ctx.lineCap = "round"
ctx.strokeStyle="yellow"
ctx.setLineDash([])
var cursorX = 0
switch (gameSpeed) {
case 1000/1:{
cursorX=8;
}break;
case 1000/2:{
cursorX=16;
}break;
case 1000/3:{
cursorX=24;
}break;
case 1000/4:{
cursorX=32;
}break;
case 1000/6:{
cursorX=40;
}break;
case 1000/8:{
cursorX=48;
}break;
case 1000/16:{
cursorX=56;
}break;
case 1000/32:{
cursorX=64;
}break;
}
if (MOUSEDOWN && LAST_MOUSE_X>=x&&LAST_MOUSE_X<=x+w && LAST_MOUSE_Y>=y-6&&LAST_MOUSE_Y<=y+6) {
var mouseCursorPos = Math.round((LAST_MOUSE_X-x)/8)*8
switch (mouseCursorPos) {
case 0:{
gameSpeed=-1;
}break;
case 8:{
gameSpeed=1000/1;
}break;
case 16:{
gameSpeed=1000/2;
}break;
case 24:{
gameSpeed=1000/3;
}break;
case 32:{
gameSpeed=1000/4;
}break;
case 40:{
gameSpeed=1000/6;
}break;
case 48:{
gameSpeed=1000/8;
}break;
case 56:{
gameSpeed=1000/16;
}break;
case 64:{
gameSpeed=1000/32;
}break;
}
}
ctx.beginPath()
ctx.moveTo(x+cursorX,y-4)
ctx.lineTo(x+cursorX,y+4)
ctx.stroke();
}
function DrawWrapText(text,x,y,w,fontHeight,ctx,topToBottom=false/*Set to true to draw from the position downward. Defaults to drawing upwards.*/) {
var arr = text.split(" ")
var finalText = []
for (var i=0;iw) {
finalText.push(arr[i])
} else {
if (finalText.length===0) {
finalText.push(arr[i])
} else {
finalText[finalText.length-1]+=" "+arr[i]
}
}
}
for (var i=0;i=0;i--) {
if (tape[i]===RED) {
numb=numb<<1;
numb=numb|0
} else
if (tape[i]===BLUE) {
numb=numb<<1;
numb=numb|1
}
}
return numb
}
function RenderTape(x,y,width,ctx,tape) {
var xOffset=width-24
var yOffset=0
var ySpacingMult=1
var ySpacing=12
if (tape.length>5*5) {
ySpacingMult=(ySpacing*5)/(Math.ceil(tape.length/5)*ySpacing)
}
yOffset=ySpacing*ySpacingMult
for (var i=5;i0) {if (HasRelativeConnection(grid.x-1,grid.y+0,RIGHT)){connections[LEFT]=true}}
if (grid.x0) {if (HasRelativeConnection(grid.x+0,grid.y-1,DOWN)){connections[UP]=true}}
if (grid.y=bounds.x&&
LAST_MOUSE_X<=bounds.x+bounds.w&&
LAST_MOUSE_Y>=bounds.y&&
LAST_MOUSE_Y<=bounds.y+bounds.h)
}
function GetArrowImage(col) {
switch (col) {
case RED:{
return ID_ARROW_R
}break;
case BLUE:{
return ID_ARROW_B
}break;
case GREEN:{
return ID_ARROW_G
}break;
case YELLOW:{
return ID_ARROW_Y
}break;
case PURPLE:{
return ID_ARROW_P
}break;
case PINK:{
return ID_ARROW_PI
}break;
case BLACK:{
return ID_ARROW_BL
}break;
case GRAY:{
return ID_ARROW_GR
}break;
}
}
function GetDotImage(col) {
switch (col) {
case RED:{
return ID_DOT_R
}break;
case BLUE:{
return ID_DOT_B
}break;
case GREEN:{
return ID_DOT_G
}break;
case YELLOW:{
return ID_DOT_Y
}break;
case PURPLE:{
return ID_DOT_P
}break;
case PINK:{
return ID_DOT_PI
}break;
case BLACK:{
return ID_DOT_BL
}break;
case GRAY:{
return ID_DOT_GR
}break;
}
}
function RenderSubmenu(ctx) {
if (SUBMENU.visible) {
ctx.fillStyle="#76abab"
var submenuRows = BUTTON_SELECTED.submenu_buttons.length/3
var submenuCols = 3
ctx.fillRect(0,canvas.height*0.8-submenuRows*48-16,submenuCols*48+16,submenuRows*48+16)
var index = 0;
for (var button of BUTTON_SELECTED.submenu_buttons) {
if (SubmenuButtonIsUnlocked(button)) {
var buttonX = ((index%3)*48)+16
var buttonY = canvas.height*0.8-(Math.floor(index/3)*48)-40
RenderIcon(buttonX,buttonY,ctx,button,ITEM_DIRECTION,(LAST_MOUSE_X>=buttonX&&LAST_MOUSE_X<=buttonX+32&&LAST_MOUSE_Y>=buttonY&&LAST_MOUSE_Y<=buttonY+32)?"#555555":"#b5b5b5")
index++;
}
}
}
}
function SubmenuButtonIsUnlocked(button) {
return gameStage.tutorial===undefined||!gameStage.tutorial||button.color1===RED||button.color1===BLUE
}
function ButtonIsUnlocked(button) {
if (button===HOME_BUTTON&&gameStage.tutorial&&!LevelIsBeat(gameStage.name)) {
return false
}
return gameStage.locked===undefined||(!gameStage.locked.includes(button))
}
function isMouseOverButton(button) {
return LAST_MOUSE_X>=button.x&&
LAST_MOUSE_X<=button.x+button.w&&
LAST_MOUSE_Y>=button.y&&
LAST_MOUSE_Y<=button.y+button.h
}
function DisplayTooltip(button,ctx) {
//240x(20*lines) (16pt font)
var tooltipSplit=[]
if (MOBILE&&button.mobileTooltip!==undefined){
tooltipSplit=button.mobileTooltip.split("\n")
} else {
tooltipSplit=button.tooltip.split("\n")
}
var tooltipBounds = {
x:Math.min(Math.max(LAST_MOUSE_X-120,0),canvas.width*0.75-240),
y:LAST_MOUSE_Y-tooltipSplit.length*20-4,
w:240,
h:tooltipSplit.length*16+8
}
ctx.globalAlpha=0.9
ctx.fillStyle="#20424a"
ctx.fillRect(tooltipBounds.x,tooltipBounds.y,tooltipBounds.w,tooltipBounds.h)
ctx.strokeStyle="white"
ctx.lineWidth=1
ctx.setLineDash([])
ctx.strokeRect(tooltipBounds.x,tooltipBounds.y,tooltipBounds.w,tooltipBounds.h)
ctx.fillStyle="white"
ctx.textAlign = "left"
for (var i=0;i=4000 || !MOBILE&&new Date().getTime()-MOUSEOVERTIME>=1000) {
mouseOverButton = button
}
}
if (button.lastselected) {
RenderIcon(buttonX,buttonY,ctx,button.lastselected,(button.cb===undefined)?ITEM_DIRECTION:0,"#b5b5b5")
} else {
AddButton(button.img,buttonX,buttonY,ctx,button,(button.cb===undefined)?ITEM_DIRECTION:0)
}
}
buttonX+=47
}
if (mouseOverButton!==undefined) {
DisplayTooltip(mouseOverButton,ctx)
}
if (!mousedOver) {
MOUSEOVERTIME=-1
}
if (gridModeIsAvailable()) {
AddButton(MOVE_BUTTON.img,MOVE_BUTTON.x,MOVE_BUTTON.y,ctx,MOVE_BUTTON,MOVE_BUTTON.cb)
}
}
}
function gridModeIsAvailable() {
return (gameGrid.length>5||(gameGrid.length>0&&gameGrid[0].length>5))&&!DELETEMODE
}
function AddButton(img,x,y,ctx,button,dir=0) {
ctx.fillStyle="#b5b5b5"
ctx.fillRect(x,y,32,32)
if (img===ID_WRITER) {
drawImage(x+16,y+16,img,ctx,dir*90-90)
} else {
drawImage(x+16,y+16,img,ctx,dir*90)
}
}
function ConsumeTape() {
BOT_TAPE=BOT_TAPE.substring(1)
}
function AppendTape(col) {
BOT_TAPE+=col
}
function OverwriteTape(col) {
BOT_TAPE=col+BOT_TAPE.substring(1)
}
function LeftOf(dir) {
return (dir+1)%4
}
function RightOf(dir) {
if (dir===0) {dir=4}
return (dir-1)%4
}