Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <!DOCTYPE html>
- <!-- blah 2016 -->
- <html>
- <head>
- <meta content="text/html;charset=utf-8" http-equiv="Content-Type">
- <style>
- canvas { /* Credit: http://stackoverflow.com/questions/7615009/disable-interpolation-when-scaling-a-canvas */
- image-rendering: optimizeSpeed;
- image-rendering: -moz-crisp-edges;
- image-rendering: -webkit-optimize-contrast;
- image-rendering: -o-crisp-edges;
- image-rendering: pixelated;
- -ms-interpolation-mode: nearest-neighbor;
- }
- .selMenuItem:hover {
- background-color:rgba(255,255,255,0.1);
- }
- .selMenuItemHighlighted {
- background-color:white;
- color:black;
- }
- .button {
- background-color:#111111;
- color:white;
- cursor:pointer;
- border:1px solid black;
- border-radius:4px;
- padding:0px 4px 0px 4px;
- }
- .button:hover {
- background-color:white;
- color:#111;
- }
- .button:active {
- background-color:#555;
- color:#ccc;
- }
- .number {
- font-family:consolas,monospace;
- }
- body { /* Credit: http://stackoverflow.com/questions/6900124/how-to-make-certain-text-not-selectable-with-css */
- -webkit-user-select: none;
- -moz-user-select: none;
- -ms-user-select: none;
- color:white;
- font-family:sans-serif;
- font-size:12px;
- background-color:black;
- margin:0;
- }
- html, body {
- height: 100%;
- }
- input {
- background-color:#000000;
- color:white;
- border:1px solid #ffff00;
- border-radius:3px;
- font-family:consolas,monospace;
- }
- input:hover {
- background-color:#111111;
- }
- textarea {
- background-color:#000000;
- color:white;
- border:1px solid #ffff00;
- border-radius:3px;
- font-family:consolas,monospace;
- font-size: 12px;
- }
- textarea:hover {
- background-color:#111111;
- }
- a {
- color:#ffffff;
- }
- h1 {
- text-align:center;
- }
- h2 {
- text-align:center;
- }
- h3 {
- margin:0;
- margin-top:4px;
- }
- p.date {
- margin:0;
- font-size:8px;
- }
- .changelist {
- margin:0;
- }
- .blah {
- font-weight:bold;
- font-size:55px;
- }
- .blah:hover {
- animation: pulse 0.5s infinite;
- color:rgba(0,0,0,0);
- }
- @keyframes pulse {
- 0% {
- text-shadow:20px 20px 5px #ff0;
- }
- 10% {
- text-shadow:10px -30px 15px #f0f;
- }
- 20% {
- text-shadow:2px 5px 10px #fff;
- }
- 30% {
- text-shadow:30px 300px 0px #777;
- }
- 50% {
- text-shadow:1px 1px 1px #f00;
- }
- 100% {
- text-shadow:20px 20px 5px #ff0;
- }
- }
- </style>
- <title>Reasoning Realm</title>
- <script>
- var worldX = 64; //Width of the world.
- var worldY = 64; //Height of the world.
- var cellList = []; //List of every cell in the world.
- var colours = []; //Colour scheme. Modified by setRule().
- var names = []; //Names belonging to those colours.
- var running = true; //You can pause the simulation using this.
- var zoom = 8; //each cell is zoom pixels squared
- var canvasHeight = 512; //Height of the canvas. So you can resize it, otherwise this variable would be useless
- var canvasWidth = 512; //See canvasHeight.
- var drawType = 1; //The value you can set pixels to with the mouse.
- var mouseIsDown = false; //Used for clicking and dragging.
- var cameraPanX = 0; //for panning the camera. What an insightful comment I know
- var cameraPanY = 0; //It's measured in pixels.
- var eorc = true; //This variable is used by the resize function. It is whether to expand or contract the world. It's global because it can get modified by a button
- var dir = 0; //Also used by the resize function. The direction of expansion.
- var rectPosY = 0; //Selection location. This is the top left corner
- var rectPosX = 0;
- var rectSizeY = 1; //Dimensions of the selection. going down right
- var rectSizeX = 1;
- var rectDragging = false; //Whether or not the player is dragging the selection, or if they're first making it
- var rectDragX = 8; //For when you drag the selection; it's the location of the cursor relative to the top left corner of the selection.
- var rectDragY = 8;
- var rectCellList = [[0,0],[0,0]];
- var rectExists = false;
- var clipboard = [ //For copy/paste functionality on the selection tool. nothing to see here move along
- [1,0,1,0,1,1,1,0,0,1,1,0,0,0,1,0,0,1,1,0], //The clipboard doesn't need length or height variables cause we can just use clipboard.length for Y and clipboard[0].length for X
- [1,1,1,0,0,1,0,0,0,1,0,1,0,1,1,1,0,1,0,1],
- [1,0,1,0,1,1,1,0,0,1,1,0,0,1,0,1,0,1,0,1]];
- var appHeight = 512; //The height of the entire app, in pixels.
- var appWidth = 512; //See appHeight
- var panning = [false,false,false,false]; //It's an array of the four directions. If the value for left is true, the camera will be panning to the left. More than one can be true at the same time. Allows fluid panning.
- var transitions = [ //Transition table. Initialised, as you can see, to B3/S23. This obviously supports generations rules if you try, but I use a different function because it's faster.
- [0,0,0,1,0,0,0,0,0],
- [0,0,1,1,0,0,0,0,0]];
- var speedDT = 1; //Optimal delta time (amount of time between frames) multiplied by 15ms
- var speedMult = 1; //Amount of iterations per frame.
- var tickCount = 0; //Used internally to work with speedDT. Like, if speedDT == 2, it'll use this to only simulate every other frame
- var rule = ""; //Used for the 'rule = whatever' part of RLE savecodes
- var pauselock = false; //dan
- var sparse = [];
- var nextSparse = [];
- var difference = [];
- var algorithm = 1; //0 is sparse algorithm, 1 is brute force
- var generation = 0; //Amount of generations since the pattern was loaded.
- var gpscount = 0;
- //That was the variables. Now we start listing out functions. The 'meat' of the script.
- //Create the world.
- for (y=0; y < worldY; y++){
- cellList[y] = [];
- for (x=0; x < worldX; x++){
- cellList[y][x] = 0;
- }
- }
- function drawWorld(){ //Render the world. never use this unless there's some clear reason to, because it draws every cell in the world.
- drawEmpty();
- for (y=0; y < worldY; y++){
- for (x=0; x < worldX; x++){
- if(cellList[y][x] > 0){
- drawCellAt(x,y);
- }
- }
- }
- }
- function drawCell(x,y,type){ //Use this if you already know what the cell's state will be (faster than having this look it up when you already know)
- d[0] = colours[type][0];
- d[1] = colours[type][1];
- d[2] = colours[type][2];
- canvas.putImageData(id,x,y);
- }
- function drawCellAt(x,y){ //Otherwise use this
- drawCell(x,y,cellList[y][x]);
- }
- function getCell(x,y){ //Is the cell at [x,y] within the field? if so return its type
- if (x > -1 && x < worldX && y > -1 && y < worldY){
- return cellList[y][x];
- } else {
- return false;
- }
- }
- function vonNeumannNeighbours(x,y,type){ //Returns amount of cells equal to type within the Von Neumann neighbourhood of x,y
- var count=0;
- if(cellList[y][x-1] == type){
- count++;
- };
- if(cellList[y-1] != undefined && cellList[y-1][x] == type){
- count++;
- };
- if(cellList[y][x+1] == type){
- count++;
- };
- if(cellList[y+1] != undefined && cellList[y+1][x] == type){
- count++;
- };
- return count;
- }
- function mooreNeighbours(x,y,type){ //Returns amount of cells equal to type within the Von Neumann neighbourhood of x,y
- var count=0;
- var bottom = cellList[y+1] != undefined;
- var top = cellList[y-1] != undefined;
- if(cellList[y][x-1] == type){
- count++;
- };
- if(top && cellList[y-1][x] == type){
- count++;
- };
- if(cellList[y][x+1] == type){
- count++;
- };
- if(bottom && cellList[y+1][x] == type){
- count++;
- };
- if(top && cellList[y-1][x-1] == type){
- count++;
- };
- if(bottom && cellList[y+1][x-1] == type){
- count++;
- };
- if(top && cellList[y-1][x+1] == type){
- count++;
- };
- if(bottom && cellList[y+1][x+1] == type){
- count++;
- };
- return count;
- }
- function putCell(x,y,type){ //This is for things where the player is editing the canvas.
- drawCell(x,y,type);
- cellList[y][x] = type;
- if(algorithm == 0){
- if(!inSparse(x,y)){
- sparse.push(y,x);
- }
- if(!inSparse(x-1,y)){
- sparse.push(y,x-1);
- }
- if(!inSparse(x+1,y)){
- sparse.push(y,x+1);
- }
- if(!inSparse(x,y-1)){
- sparse.push(y-1,x);
- }
- if(!inSparse(x+1,y-1)){
- sparse.push(y-1,x+1);
- }
- if(!inSparse(x-1,y-1)){
- sparse.push(y-1,x-1);
- }
- if(!inSparse(x,y+1)){
- sparse.push(y+1,x);
- }
- if(!inSparse(x+1,y+1)){
- sparse.push(y+1,x+1);
- }
- if(!inSparse(x-1,y+1)){
- sparse.push(y+1,x-1);
- }
- }
- }
- function setCell(x,y,type){ //This is for the tick function. It's efficient.
- drawCell(x,y,type);
- difference.push(y,x,type);
- if(algorithm == 0){
- padSparse(x,y);
- }
- }
- function padSparseMoore(x,y){
- if(!inSparse(x,y)){
- nextSparse.push(y,x);
- }
- if(!inSparse(x-1,y)){
- nextSparse.push(y,x-1);
- }
- if(!inSparse(x+1,y)){
- nextSparse.push(y,x+1);
- }
- if(!inSparse(x,y-1)){
- nextSparse.push(y-1,x);
- }
- if(!inSparse(x+1,y-1)){
- nextSparse.push(y-1,x+1);
- }
- if(!inSparse(x-1,y-1)){
- nextSparse.push(y-1,x-1);
- }
- if(!inSparse(x,y+1)){
- nextSparse.push(y+1,x);
- }
- if(!inSparse(x+1,y+1)){
- nextSparse.push(y+1,x+1);
- }
- if(!inSparse(x-1,y+1)){
- nextSparse.push(y+1,x-1);
- }
- }
- function padSparseJvN(x,y){
- if(!inSparse(x,y)){
- nextSparse.push(y,x);
- }
- if(!inSparse(x-1,y)){
- nextSparse.push(y,x-1);
- }
- if(!inSparse(x+1,y)){
- nextSparse.push(y,x+1);
- }
- if(!inSparse(x,y-1)){
- nextSparse.push(y-1,x);
- }
- if(!inSparse(x,y+1)){
- nextSparse.push(y+1,x);
- }
- }
- function applyDifference(){
- for(i=0;i<difference.length;i+=3){
- cellList[difference[i]][difference[i+1]] = difference[i+2];
- }
- }
- function inSparse(x,y){
- for(ib=0;ib<nextSparse.length;ib+=2){
- if(nextSparse[ib] == y && nextSparse[ib+1] == x){
- return true;
- }
- }
- return false;
- }
- function fillSparse(){
- for(y=0;y<worldY;y++){
- for(x=0;x<worldX;x++){
- sparse.push(y,x);
- }
- }
- }
- function transLife(x,y){
- return transitions[cellList[y][x]][mooreNeighbours(x,y,1)];
- }
- function transGenerations(x,y){
- cell = cellList[y][x];
- if(cell<2){
- return transitions[cell][mooreNeighbours(x,y,1)];
- } else if(cell==colours.length-1) {
- return 0;
- } else {
- return cell+1;
- }
- }
- function transLL(x,y){ //Logic land transition function
- cell = cellList[y][x];
- if(cell == 0){ //Empty Space
- //Do nothing. Faster than running through all the other conditions, even if it requires an empty block.
- } else if(cell == 1 && (vonNeumannNeighbours(x,y,2) > 0 || vonNeumannNeighbours(x,y,16) > 0 )){ //Inactive Wire
- return 2;
- } else if(cell == 2 && (vonNeumannNeighbours(x,y,3) > 0 || vonNeumannNeighbours(x,y,10))){ //Active Wire
- return 3;
- } else if(cell == 3){ //Inhibited Wire
- return 1;
- } else if(cell == 4){ //Cross Over. This one is complex and inefficient, so don't use Crossovers to speed up wires, cause it slows the simulation down in the long term.
- if(x>0 && x < worldX-1 && y>0 && y < worldY-1 ){
- friends = [cellList[y][x-1], cellList[y-1][x], cellList[y][x+1], cellList[y+1][x]];
- friendLocs = [y,x-1,y-1,x,y,x+1,y+1,x];
- for(i=0;i<4;i++){
- if(friends[i] == 2 && friends[(i+2)%4] == 1){
- setCell(friendLocs[((i+2)%4)*2+1],friendLocs[((i+2)%4)*2],2);
- }
- if(friends[i] == 3 && friends[(i+2)%4] == 2){
- setCell(friendLocs[((i+2)%4)*2+1],friendLocs[((i+2)%4)*2],3);
- }
- }
- }
- return 4;
- } else if(cell > 4 && cell < 9 && vonNeumannNeighbours(x,y,2) > 0){ //Inactive gates
- return cell+6;
- } else if(cell > 10 && cell < 15 && vonNeumannNeighbours(x,y,2) == 0){ //Active gates
- return cell-6;
- } else if(cell == 15 && vonNeumannNeighbours(x,y,3) > 0){ //Active T-flip flop
- return 9;
- } else if(cell == 9 && vonNeumannNeighbours(x,y,3) > 0){ //Inactive T-flip flop
- return 15;
- } else if(cell == 10 || cell == 16){ //Gate output
- if(vonNeumannNeighbours(x,y,11) > 0 || vonNeumannNeighbours(x,y,5) > 0){ //NOR
- if(vonNeumannNeighbours(x,y,11) == 0){
- return 16;
- } else {
- return 10;
- }
- } else if(vonNeumannNeighbours(x,y,12) > 0 || vonNeumannNeighbours(x,y,6) > 0){ //OR
- if(vonNeumannNeighbours(x,y,12) > 0){
- return 16;
- } else {
- return 10;
- }
- } else if(vonNeumannNeighbours(x,y,13) > 0 || vonNeumannNeighbours(x,y,7) > 0){ //XOR
- if(vonNeumannNeighbours(x,y,13) == 1){
- return 16;
- } else {
- return 10;
- }
- } else if(vonNeumannNeighbours(x,y,14) > 0 || vonNeumannNeighbours(x,y,8) > 0){ //AND
- if(vonNeumannNeighbours(x,y,8) == 0){
- return 16;
- } else {
- return 10;
- }
- } else if(vonNeumannNeighbours(x,y,15) > 0 || vonNeumannNeighbours(x,y,9) > 0){ //T flip flop
- if(vonNeumannNeighbours(x,y,9) > 0){
- return 10;
- } else {
- return 16;
- }
- }
- }
- return cell;
- }
- var transFunc = transLL;
- function tick(){
- if(!pauselock){
- for(ilk=0;ilk<speedMult;ilk++){
- generation++;
- document.getElementById("genNum").innerHTML = generation;
- difference = [];
- if(algorithm == 0){
- nextSparse = [];
- for(il=0;il<sparse.length;il+=2){
- var y = sparse[il];
- var x = sparse[il+1];
- if(y>-1 && y<worldY && x>-1 && x<worldX){
- var newCell = transFunc(x,y);
- if(newCell != cellList[y][x]){
- setCell(x,y,newCell);
- }
- }
- }
- sparse = nextSparse.slice();
- } else if(algorithm == 1){
- for(y=0;y<worldY;y++){
- for(x=0;x<worldX;x++){
- var newCell = transFunc(x,y);
- if(newCell != cellList[y][x]){
- setCell(x,y,newCell);
- }
- }
- }
- }
- applyDifference();
- }
- }
- }
- function mouseDown(event){ //Event called when the mouse gets first pressed
- mouseIsDown = true;
- cellx = Math.floor((event.clientX - crect.left)/zoom);
- celly = Math.floor((event.clientY - crect.top)/zoom);
- if(drawType == -1){
- if(cellx >= rectPosX && celly >= rectPosY && cellx <= rectPosX+rectSizeX && celly <= rectPosY+rectSizeY && rectExists){ //If you clicked on the selection
- rectDragging = true;
- rectDragX = cellx-rectPosX;
- rectDragY = celly-rectPosY;
- } else {
- finishSelection();
- rectExists = false;
- rectDragging = false;
- rectPosX = cellx;
- rectPosY = celly;
- rectSizeX = -1;
- rectSizeY = -1;
- }
- } else {
- if (cellx > -1 && cellx < worldX && celly > -1 && celly < worldY){ //If the mouse is over the play area
- putCell(cellx,celly,drawType);
- }
- }
- }
- function mouseMove(event){ //When the mouse moves (not just over the canvas, which is how you can draw behind the ui)
- cellx = Math.floor((event.clientX - crect.left)/zoom); //Mathematics to get the cell you clicked on.
- celly = Math.floor((event.clientY - crect.top)/zoom);
- if (mouseIsDown && (cellx > -1 && cellx < worldX && celly > -1 && celly < worldY)){ //If the mouse is over the play area and the mouse is down
- if(drawType > -1){ //If it's not the rectangle select tool, which is -1
- putCell(cellx,celly,drawType);
- } else if(!rectDragging) { //Selection gets resized as the user drags the mouse
- unDrawSelectionBox();
- rectSizeX = cellx-rectPosX;
- rectSizeY = celly-rectPosY;
- drawSelectionBox();
- displaySelectionDim();
- } else { //Selection gets moved as player drags the mouse
- if(cellx-rectDragX > -1 && celly-rectDragY > -1 && (cellx-rectDragX)+rectSizeX < worldX && (celly-rectDragY)+rectSizeY < worldY){ //If the selection would be moved to a place inside the world.
- unDrawSelectionBox();
- unDrawSelection();
- rectPosX = cellx-rectDragX;
- rectPosY = celly-rectDragY;
- drawSelectionBox();
- drawSelection();
- }
- }
- }
- }
- function mouseUp(event){
- mouseIsDown = false;
- if(drawType == -1 && !rectDragging){ //selection tool, finish making the selection
- if(rectSizeX < 0){ //If the size is negative make it positve and move it over so our loops can handle it
- rectPosX += rectSizeX;
- rectSizeX = rectSizeX-rectSizeX-rectSizeX;
- }
- if(rectSizeY < 0){
- rectPosY += rectSizeY;
- rectSizeY = rectSizeY-rectSizeY-rectSizeY;
- }
- rectCellList = []; //We will create a list, rectCellList, of all the selected cells. This is useful
- for(y=0;y<=rectSizeY;y++){
- rectCellList[y] = [];
- for(x=0;x<=rectSizeX;x++){
- rectCellList[y][x] = cellList[y+rectPosY][x+rectPosX];
- cellList[y+rectPosY][x+rectPosX] = 0;
- }
- }
- rectExists = true;
- }
- }
- //don't expect very useful comments if you try to figure out how the selection tool works. just expect "uhh"
- function unDrawSelection(){
- for(y=0;y<=rectSizeY;y++){ //uhh
- for(x=0;x<=rectSizeX;x++){
- if(rectCellList[y][x] != 0){
- drawCellAt(x+rectPosX, y+rectPosY);
- }
- }
- }
- }
- function drawSelection(){
- for(y=0;y<=rectSizeY;y++){ //uhh
- for(x=0;x<=rectSizeX;x++){
- if(rectCellList[y][x] != 0){
- drawCell(x+rectPosX, y+rectPosY,rectCellList[y][x]);
- }
- }
- }
- }
- function finishSelection(){
- if(rectExists){
- unDrawSelectionBox();
- for(y=0;y<=rectSizeY;y++){ //uhh
- for(x=0;x<=rectSizeX;x++){
- if(rectCellList[y][x] != 0){
- putCell(x+rectPosX, y+rectPosY, rectCellList[y][x]);
- }
- }
- }
- }
- }
- function drawSelectionBox(){ //It's poorly made but it works
- canvas.beginPath();
- canvas.strokeStyle = "#008888";
- canvas.fillStyle = "#008888";
- canvas.fillRect(rectPosX,rectPosY,1,1); //I have to fill in the corners myself because drawRect is antialiased. Innefficient I know
- canvas.fillRect(rectPosX+rectSizeX,rectPosY,1,1);
- canvas.fillRect(rectPosX,rectPosY+rectSizeY,1,1);
- canvas.fillRect(rectPosX+rectSizeX,rectPosY+rectSizeY,1,1);
- canvas.rect(rectPosX+0.5,rectPosY+0.5,rectSizeX,rectSizeY);
- canvas.stroke();
- }
- function unDrawSelectionBox(){ //replaces the pixels of the selection with the cells behind them. Simply put, undraws the selection box. Called when it has to be resized or moved or replaced or etc
- for(y=0;y<=Math.abs(rectSizeY);y++){
- if(rectSizeY >= 0){
- drawCellAt(rectPosX, y+rectPosY);
- drawCellAt(rectPosX+rectSizeX, y+rectPosY);
- } else {
- drawCellAt(rectPosX, rectPosY-y);
- drawCellAt(rectPosX+rectSizeX, rectPosY-y);
- }
- }
- for(x=0;x<=Math.abs(rectSizeX);x++){
- if(rectSizeX >= 0){
- drawCellAt(x+rectPosX, rectPosY);
- drawCellAt(rectPosX+x, rectPosY+rectSizeY);
- } else {
- drawCellAt(rectPosX-x, rectPosY);
- drawCellAt(rectPosX-x, rectPosY+rectSizeY);
- }
- }
- }
- function displaySelectionDim(){
- document.getElementById("rectdisy").innerHTML = Math.abs(rectSizeY)+1;
- document.getElementById("rectdisx").innerHTML = Math.abs(rectSizeX)+1;
- }
- function changeZoom(n){ //It scales the canvas itself.
- if(n>0){ //We don't want to be able to set zoom to 0, or -1, or whatever
- zoom = n;
- c.style.width = worldX * n + "px"; //Note the difference between c.width and c.style.width. This is only how big it appears.
- c.style.height = worldY * n + "px";
- document.getElementById("zoomCounter").innerHTML=zoom; //Display the level of zoom. Pretty obvious
- moveCanvas()
- }
- }
- function changeDT(newDT){
- if(newDT > 0){
- speedDT = newDT;
- document.getElementById("DTcounter").innerHTML = speedDT*15+"ms ("+Math.round(1000/(speedDT*15))+"fps)";
- }
- }
- function changeSpeedMult(newmult){
- if(newmult > 0){
- speedMult = newmult;
- document.getElementById("multCounter").innerHTML = speedMult;
- }
- }
- function changeDrawType(newDrawType){ //Called by the items in the menu to the top left
- if(newDrawType<names.length){
- if(drawType == -1){
- finishSelection();
- rectExists = false;
- }
- document.getElementById("sel"+drawType).className = "selMenuItem";
- document.getElementById("sel"+newDrawType).className = "selMenuItemHighlighted";
- drawType = newDrawType;
- }
- }
- function pausePlay(){ //This function pauses the sim if it's playing, plays it if it's paused
- if(!pauselock){
- running = !running;
- if(running){
- document.getElementById("pauseDisplay").innerHTML = "Pause";
- } else {
- document.getElementById("pauseDisplay").innerHTML = "Unpause";
- }
- }
- }
- function pauseLock(){
- pauselock = !pauselock;
- if(pauselock){
- document.getElementById("pauseLockDisplay").innerHTML = "Unlock";
- } else {
- document.getElementById("pauseLockDisplay").innerHTML = "Lock";
- }
- }
- //PLAGIARISED SAVECODE FUNCTIONS. cred to jack e (He didn't leave many comments on his code. I indented it myself)
- var saveCodeCharacterSet = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
- var LLRRdiff = [0,1,2,3,4,5,11,6,12,7,13,8,14,9,15,10,16]; //Used to convert between Logic Land's cell values and Reasoning Realm's
- var RRLLdiff = [0,1,2,3,4,5,7,9,11,13,15,6,8,10,12,14,16]; //Used to convert the other way. It's the inverse
- function convertNumberIntoSaveCodeCharacter(number){
- return saveCodeCharacterSet.substring(number, number + 1);
- }
- function convertSaveCodeCharacterIntoNumber(character){
- return saveCodeCharacterSet.indexOf(character);
- }
- function convertSaveCodeIntoNumberList(text){
- var output = [];
- var index = 0;
- while (index < text.length){
- var tempCharacter = text.substring(index, index + 1);
- if (tempCharacter == "*"){
- tempCharacter = text.substring(index + 1, index + 2);
- var count = convertSaveCodeCharacterIntoNumber(tempCharacter);
- tempCharacter = text.substring(index + 2, index + 3);
- var tempNumber = convertSaveCodeCharacterIntoNumber(tempCharacter);
- while (count > 0){
- output.push(tempNumber);
- count -= 1;
- }
- index += 3;
- } else {
- var tempNumber = convertSaveCodeCharacterIntoNumber(tempCharacter);
- output.push(tempNumber);
- index += 1;
- }
- }
- //BEGIN SELF INSERT YEAH. This part converts the cell list of Logic Land, which seems to be 1D, to the two dimensional array format Reasoning Realm uses
- //It also converts between the cell type values.
- newOutput = [];
- for(y=0;y<worldY;y++){
- newOutput[y] = [];
- for(x=0;x<worldX;x++){
- newOutput[y][x] = LLRRdiff[output[y*worldX+x]];
- }
- }
- //END SELF INSERT NO
- return newOutput;
- }
- function setSaveCode(text){
- if(running){ //This is a self insert to the plagiarized loading thing that makes the thing paused
- pausePlay();
- }
- var startIndex = 0;
- while (text.substring(startIndex, startIndex + 1) == " ")
- {
- startIndex += 1;
- }
- var endIndex = text.length;
- while (text.substring(endIndex - 1, endIndex) == " ")
- {
- endIndex -= 1;
- }
- var tempIndex = text.indexOf("/");
- var tempList = text.substring(0, tempIndex).split(",");
- worldX = parseInt(tempList[0]);
- worldY = parseInt(tempList[1]);
- c.height = worldY;
- c.width = worldX;
- c.style.height = worldY*zoom+"px";
- c.style.width = worldX*zoom+"px";
- var tempText = text.substring(tempIndex + 1, text.length);
- cellList = convertSaveCodeIntoNumberList(tempText);
- drawWorld();
- //Set the world dimension display
- document.getElementById("worldXDisplay").innerHTML = worldX;
- document.getElementById("worldYDisplay").innerHTML = worldY;
- }
- function convertNumberListIntoSaveCode(numberList){
- var output = "";
- var startIndex = 0;
- var endIndex = 0;
- while (startIndex < numberList.length){
- endIndex = startIndex + 1;
- var startNumber = numberList[startIndex];
- var tempCharacter = convertNumberIntoSaveCodeCharacter(startNumber);
- var count = 1;
- while (endIndex < numberList.length && numberList[endIndex] == startNumber && count < saveCodeCharacterSet.length - 1){
- endIndex += 1;
- count += 1;
- }
- if (count < 4){
- while (count > 0){
- output += tempCharacter;
- count -= 1;
- }
- } else {
- var tempCharacter2 = convertNumberIntoSaveCodeCharacter(count);
- output += "*" + tempCharacter2 + tempCharacter;
- }
- startIndex = endIndex;
- }
- return output;
- }
- function displaySaveCode(){
- var saveCode = worldX + "," + worldY + "/";
- //SELF INSERT AGAIN. Just a reversed version of the other one. Converts 2d array into 1d so Jack's code will work on it
- newInput = [];
- for(y=0;y<worldY;y++){
- for(x=0;x<worldX;x++){
- newInput[y*worldX+x] = RRLLdiff[cellList[y][x]]; //Convert between value types and all
- }
- }
- //END SELF INSERT
- saveCode += convertNumberListIntoSaveCode(newInput);
- document.getElementById("RRFbox").value = saveCode;
- }
- //END PLAGIARY
- function keyPress(event){ //When a key is pressed, called by body.
- var key = event.which;
- if(key == 32){ //Space (pause)
- pausePlay();
- } else if(key == 37 || key == 65){ //Left cursor key (pan left)
- startPan(0);
- } else if(key == 38 || key == 87){ //Up cursor key (pan up)
- startPan(1);
- } else if(key == 39 || key == 68){ //Right cursor key (pan right)
- startPan(2);
- } else if(key == 40 || key == 83){ //Down cursor key (pan down)
- startPan(3);
- } else if(key >= 48 && key <= 57){ //Number keys (set drawType)
- changeDrawType(key-48);
- } else if(key == 82){ //R key (select tool)
- if(drawType != -1){
- changeDrawType(-1);
- }
- } else if(key == 79){ //O key (zoom out)
- if(zoom>1){
- changeZoom(zoom-1);
- }
- } else if(key == 73){ //I key (zoom in)
- changeZoom(zoom+1);
- } else if(key == 70){ //F key (simulate one frame)
- if(!pauselock){tick()};
- } else if(key == 67){ //C key (copy selection to clipboard)
- if(drawType == -1){
- clipboard = JSON.parse(JSON.stringify(rectCellList)); //Makes clipboard into a deep clone of rectCellList
- }
- } else if(key == 86){ //V key (paste clipboard to selection)
- if(drawType == -1){
- if(clipboard[0].length>worldX || clipboard.length>worldY){
- alert("You tried to paste from the clipboard, but the world is too small to fit the contents of the clipboard. It doesn't matter where you tried to paste it, you have to expand the world. :(");
- return 0;
- }
- finishSelection();
- rectCellList = JSON.parse(JSON.stringify(clipboard)); //Does the exact reverse of C
- rectSizeX = clipboard[0].length-1;
- rectSizeY = clipboard.length-1;
- displaySelectionDim();
- if(rectPosX+rectSizeX>worldX){ //Error-proofing; this means that the selection box will jump to the top left if its bounding box intersects the abyss. you could still probably resize the world to be smaller than the clipboard, but who would do that?
- rectPosX = 0;
- }
- if(rectPosY+rectSizeY>worldY){
- rectPosY = 0;
- }
- drawSelectionBox();
- drawSelection();
- }
- } else if (key == 69 || key == 88){ //E+X keys (Clear/cut selection)
- if(drawType == -1){
- if(key == 88){ //X (cut)
- clipboard = JSON.parse(JSON.stringify(rectCellList)); //Makes clipboard into a deep clone of rectCellList
- } //E and X both clear. that's what this part does:
- unDrawSelection();
- drawSelectionBox();
- for(y=0;y<=rectSizeY;y++){
- for(x=0;x<=rectSizeX;x++){
- rectCellList[y][x] = 0;
- }
- }
- }
- } else if(key == 173){ //- key (gate output)
- changeDrawType(10);
- } else if(key == 219){ //[ key (half zoom)
- changeZoom(Math.round(zoom/2));
- } else if(key == 221){ //] key (double zoom)
- changeZoom(zoom*2);
- } else if(key == 77){ //M key (mirror)
- if(drawType == -1){
- var newList = []
- unDrawSelection();
- for(y=0;y<=rectSizeY;y++){
- newList[rectSizeY-y] = rectCellList[y].slice();
- }
- for(y=0;y<=rectSizeY;y++){
- rectCellList[y] = newList[y].slice();
- }
- drawSelectionBox();
- drawSelection();
- }
- } else if(key == 78){ //N key (mirror)
- if(drawType == -1){
- var newList = [];
- unDrawSelection();
- for(y=0;y<=rectSizeY;y++){
- newList[y] = [];
- for(x=0;x<=rectSizeX;x++){
- newList[y][rectSizeX-x] = rectCellList[y][x];
- }
- }
- for(y=0;y<=rectSizeY;y++){
- rectCellList[y] = newList[y].slice();
- }
- drawSelectionBox();
- drawSelection();
- }
- } else if(key == 84){ //T key (rotate)
- if(drawType == -1){
- var newList = [];
- unDrawSelection();
- unDrawSelectionBox();
- for(y=0;y<=rectSizeY;y++){
- newList[y] = [];
- for(x=0;x<=rectSizeX;x++){
- newList[y][rectSizeX-x] = rectCellList[y][x];
- }
- }
- for(y=0;y<=rectSizeY;y++){
- rectCellList[y] = newList[y].slice();
- }
- var newList = [];
- for(y=0;y<=rectSizeX;y++){
- newList[y] = [];
- for(x=0;x<=rectSizeY;x++){
- newList[y][x] = rectCellList[x][y];
- }
- }
- for(y=0;y<=rectSizeY;y++){
- rectCellList[y] = newList[y].slice();
- }
- rectSizeY = rectCellList.length-1;
- rectSizeX = rectCellList[0].length-1;
- drawSelectionBox();
- drawSelection();
- }
- } else if(key == 81){ //Q key (empty space)
- changeDrawType(0);
- } else if(key == 72){ //H key (center camera)
- centerCamera();
- }
- }
- function keyUp(event){
- var key = event.which;
- if(key == 37 || key == 65){ //Left cursor key (pan left)
- endPan(0);
- } else if(key == 38 || key == 87){ //Up cursor key (pan up)
- endPan(1);
- } else if(key == 39 || key == 68){ //Right cursor key (pan right)
- endPan(2);
- } else if(key == 40 || key == 83){ //Down cursor key (pan down)
- endPan(3);
- }
- }
- function startPan(dir){
- panning[dir] = true;
- }
- function endPan(dir){
- panning[dir] = false;
- }
- function continuousPan(){ //This is called every 50 milliseconds by the script at the end of this file.
- if(panning[0] || panning[1] || panning[2] || panning[3]){
- var panAmt = 128/zoom;
- if(panning[0]){
- cameraPanX += panAmt;
- }
- if(panning[1]){
- cameraPanY += panAmt;
- }
- if(panning[2]){
- cameraPanX -= panAmt;
- }
- if(panning[3]){
- cameraPanY -= panAmt;
- }
- moveCanvas();
- }
- }
- function pan(dir){ //This one is used by the arrow buttons in the menu.
- var panAmt = 128/zoom;
- if(dir == 0){
- cameraPanX += panAmt;
- } else if(dir == 1){
- cameraPanY += panAmt;
- } else if(dir == 2){
- cameraPanX -= panAmt;
- } else if(dir == 3){
- cameraPanY -= panAmt;
- }
- moveCanvas();
- }
- function resize(){ //Expand/contract (depending on eorc) in direction "dir" by amount "amt"
- var amt = Number(document.getElementById('expandAmount').value); //The number in the text box for the amount.
- var tempList = []; //This array is concatenated with cellList and/or its subarrays to expand the world
- //A large collection of ifs and elseifs that change the dimensions of the world, and then some operations to make sure it's drawn and rendered properly
- //Due to the nature of the 2D array, it turns out the elseif chain isn't that repetitive
- if(eorc){
- //Expand left:
- if(dir == 0){
- for(i=0;i<amt;i++){
- tempList[i] = 0;
- }
- for(y=0;y<worldY;y++){ //Templist, which is a row of amt 0s, gets concatenated with each row. horizontal expansion.
- cellList[y] = tempList.concat(cellList[y]);
- }
- worldX += amt;
- //Expand up:
- } else if(dir == 1){
- for(y=0;y<amt;y++){ //Templist will become a bunch of rows
- tempList[y] = [];
- for(x=0;x<worldX;x++){
- tempList[y][x] = 0;
- }
- }
- worldY += amt;
- cellList = tempList.concat(cellList); //templist gets concatenated to cellList
- //Expand right:
- } else if(dir == 2){
- for(i=0;i<amt;i++){
- tempList[i] = 0;
- }
- for(y=0;y<worldY;y++){
- cellList[y] = cellList[y].concat(tempList);
- }
- worldX += amt;
- //Expand down:
- } else if(dir == 3){
- for(y=0;y<amt;y++){ //this is line 420 right now lol
- cellList[worldY+y] = [];
- for(x=0;x<worldX;x++){
- cellList[worldY+y][x] = 0;
- }
- }
- worldY += amt;
- }
- } else {
- //Contract from left:
- if(dir == 0){
- for(y=0;y<worldY;y++){
- cellList[y] = cellList[y].slice(amt);
- }
- worldX -= amt;
- //contract from top:
- } else if(dir == 1){
- cellList = cellList.slice(amt);
- worldY -= amt;
- //Contract from right:
- } else if(dir == 2){
- for(y=0;y<worldY;y++){
- cellList[y] = cellList[y].slice(0,worldX-amt);
- }
- worldX -= amt;
- //Contract from bottom:
- } else if(dir == 3){
- cellList = cellList.slice(0, worldY-amt);
- worldY -= amt;
- }
- }
- c.height = worldY; //Estabish logical dimensions of the canvas after the resize.
- c.width = worldX;
- changeZoom(zoom); //The changezoom function does some things we're too lazy to put here
- drawWorld(); //Then we probably have to redraw, to be sure
- document.getElementById("worldXDisplay").innerHTML = worldX;
- document.getElementById("worldYDisplay").innerHTML = worldY;
- rectPosX = 0;
- rectPosY = 0;
- rectSizeX = 0;
- rectSizeY = 0;
- rectCellList = [[0]];
- if(algorithm == 0){
- fillSparse();
- }
- }
- function eorcf(){ //This function gets used by the button that expands or contracts the world. the f is for function
- eorc = !eorc;
- if(eorc){
- document.getElementById("eorcDisplay").innerHTML = "Expand";
- } else {
- document.getElementById("eorcDisplay").innerHTML = "Contract";
- }
- }
- var Dirnames = ["Left","Top","Right","Bottom"]; //Used to set the text of the direction button on the resize menu
- function dirf(){ //The direction of expanding/contracting. the f is for function
- dir = (dir+1) % 4; //Cycles dir between 0,1,2,3
- document.getElementById("dirDisplay").innerHTML = Dirnames[dir]; //Use of Dirnames.
- }
- function moveCanvas(){
- c.style.left = Math.round(((worldX+cameraPanX)/2 *zoom)+ appWidth/2 - worldX*zoom) + "px";
- c.style.top = Math.round(((worldY+cameraPanY)/2 *zoom)+ appHeight/2 - worldY*zoom) + "px";
- crect = c.getBoundingClientRect(); //We know that this will change crect so we can reestablish it now
- }
- function clearWorld(){
- var row = [];
- for(x=0;x<worldX;x++){
- row[x] = 0;
- }
- cellList = [];
- for(y= -1;y<=worldY;y++){
- cellList[y] = row.slice();
- }
- drawEmpty();
- }
- function drawEmpty(){ //Draws the colour of state 0 everywhere. Useful in some places.
- canvas.fillStyle = "rgb("+colours[0]+")";
- canvas.fillRect(0,0,worldX,worldY);
- }
- var rleValues = ".ABCDEFGHIJKLMNOPQRSTUVWXYZ";
- function getRLE(subject,height,width){ //Not a very elegant function
- var output = "";
- var runLength = 0;
- var runType = 0;
- var lineRunLength = 1;
- var lineRunning = true;
- var line = "";
- for(y=0;y<height;y++){
- runLength = 0;
- lineRunning = true;
- line = "";
- for(x=0;x<width;x++){
- if(subject[y][x] != 0){
- lineRunning = false;
- }
- if(runType != 0 || x+1 != width){
- if(runLength == 0){
- runType = subject[y][x];
- runLength++;
- } else if(subject[y][x] == runType){
- runLength++;
- } else {
- if(runLength>1){
- line = line+runLength+rleValues.charAt(runType);
- } else {
- line = line+rleValues.charAt(runType);
- }
- runType = subject[y][x];
- runLength = 1;
- }
- }
- }
- if(subject[y][width-1] > 0 && subject[y][width] != runType){
- if(runLength>1){
- line = line+runLength+rleValues.charAt(runType);
- } else {
- line = line+rleValues.charAt(runType);
- }
- }
- if(subject[y][width-1] > 0){
- if(runLength>1){
- line = line+runLength+rleValues.charAt(subject[y][width-1]);
- } else {
- line = line+rleValues.charAt(subject[y][width-1]);
- }
- }
- if(lineRunning){
- if(y>0){
- lineRunLength++;
- }
- } else if(lineRunLength != 0){
- if(lineRunLength > 1){
- output = output+lineRunLength+"$"+line;
- } else {
- if(y>0){
- output = output+"$"+line;
- } else {
- output = output+line;
- }
- }
- lineRunLength = 1;
- }
- }
- output="x = "+width+", y = "+height+", rule = "+rule+"\n"+output;
- output=output+"!";
- document.getElementById('RLEbox').value = output;
- }
- function setRLE(text){
- var input = text.split("\n").filter(function(line){return line != "" && line.substring(0,1) != "#"});
- input[0] = input[0].replace(/ /g, "").split(",");
- worldX = Number(input[0][0].slice(2));
- worldY = Number(input[0][1].slice(2));
- if(input[0].length > 2){
- setRule(input[0][2].slice(5));
- } else {
- setRule("B3/S23")
- }
- input[0] = "";
- input = input.join("");
- c.height = worldY;
- c.width = worldX;
- c.style.height = worldY*zoom+"px";
- c.style.width = worldX*zoom+"px";
- clearWorld();
- document.getElementById("worldXDisplay").innerHTML = worldX;
- document.getElementById("worldYDisplay").innerHTML = worldY;
- var numberHoldString = "";
- var numberHoldInt = 1;
- var headX = 0;
- var headY = 0;
- var unterminated = true;
- for(index=0;index<input.length && unterminated;index++){
- var charat = input.charAt(index);
- if(charat == "$"){
- headX = 0;
- headY += numberHoldInt;
- } else if(charat == "." || charat == "b"){
- headX += numberHoldInt;
- } else if(charat == "o" || charat == "A"){
- for(i=0;i<numberHoldInt && headX < worldX;i++){
- cellList[headY][headX] = 1;
- drawCell(headX,headY,1);
- headX++;
- }
- } else {
- var charatloc = rleValues.indexOf(charat)
- if(charatloc > 1){
- for(i=0;i<numberHoldInt;i++){
- cellList[headY][headX] = charatloc;
- drawCell(headX,headY,charatloc);
- headX++;
- }
- } else if(charat == "!"){
- unterminated = true;
- }
- }
- if(input.charCodeAt(index) >= 48 && input.charCodeAt(index) <= 57){
- numberHoldString=numberHoldString+charat;
- numberHoldInt = Number(numberHoldString);
- } else {
- numberHoldInt = 1;
- numberHoldString = "";
- }
- }
- moveCanvas();
- fillSparse();
- generation = 0;
- document.getElementById("genNum").innerHTML = 0;
- }
- function setRule(newRulestring){
- var rulestring = newRulestring.toLowerCase();
- if(rulestring == "logicland"){ //Logic Land
- colours = [[150,230,230],[225,128,50],[255,255,0],[110,64,25],[128,128,128],[150,0,150],[255,255,255],[0,0,255],[255,0,0],[0,128,0],[0,0,0],[150,0,150],[255,255,255],[0,0,255],[255,0,0],[0,128,0],[0,0,0]];
- names = ["Empty Space","Inactive Wire","Active Wire","Inhibited Wire","Cross Over","NOR Gate","OR Gate","XOR Gate","AND Gate","T Flip Flop","Gate Output"];
- createSelMenu();
- rule = newRulestring;
- transFunc = transLL;
- padSparse = padSparseJvN;
- } else { //Try to interpret the rulestring as lifelike or generations.
- var splitrule = rulestring.split("/");
- if(splitrule.length == 2) { //Lifelike rules
- transFunc = transLife;
- var bloc = rulestring.indexOf("b");
- var sloc = rulestring.indexOf("s");
- if(bloc>=sloc){ //Figuring out the order of the survival and birth conditions in the rulestring.
- splitrule[2] = splitrule[0];
- splitrule[0] = splitrule[1];
- splitrule[1] = splitrule[2];
- }
- rule = newRulestring;
- colours = [[255,255,255],[0,0,0]];
- names = ["Dead Cell","Live Cell"];
- createSelMenu();
- for(t=0;t<=1;t++){
- for(n=0;n<=8;n++){
- if(splitrule[t].includes(n)){
- transitions[t][n] = 1;
- } else {
- transitions[t][n] = 0;
- }
- }
- }
- transFunc = transLife;
- padSparse = padSparseMoore;
- } else if(splitrule.length == 3){ //Generations rules
- var states = Number(splitrule[2]);
- if(states != states || states < 3){ //NaN is not equal to itself. If that's true it means it's NaN
- alert("FUCK! THE RULE IS INVALID BECAUSE THERE IS NO _PROPER_ THIRD PARAMETER FOR THE GENERATIONS RULE BRICKFACE DUMBCUNT ITS THE AMOUNT OF STATES. idiot");
- return 0;
- }
- //Now we know this is a rule we can interpret properly
- rule = newRulestring;
- splitrule[-1] = splitrule[0]; //We have to swap b and s because I'm lazy
- splitrule[0] = splitrule[1];
- splitrule[1] = splitrule[-1];
- for(t=0;t<=1;t++){
- for(n=0;n<=8;n++){
- if(splitrule[t].includes(n)){
- transitions[t][n] = 1;
- } else {
- transitions[t][n] = 0+t+t;
- }
- }
- }
- colours = [[10,10,10],[0,0,255]];
- names = ["Dead Cell","Live Cell"];
- for(i=2;i<states;i++){
- var hue = Math.round(255*(i/states));
- colours[i] = [Math.floor(hue/3),hue,0];
- names[i] = "Refractory";
- }
- createSelMenu();
- transFunc = transGenerations;
- padSparse = padSparseMoore;
- }
- }
- drawType = 0;
- changeDrawType(drawType);
- }
- function createSelMenu(){
- selMenu.innerHTML="";
- for(i=0;i<names.length;i++){ //This loop generates the menu.
- selMenu.innerHTML = selMenu.innerHTML +
- '<div onmousedown="changeDrawType('+i+')" id="sel'+i+'" class="selMenuItem"><span style="background-color:rgb('+colours[i]+');padding:0px 4px 0px 4px;"> </span>'+i+' '+names[i]+'</div>';
- }
- }
- function centerCamera(){
- cameraPanX=0;
- cameraPanY=0;
- moveCanvas();
- }
- function changeAlgorithm(){
- if(algorithm == 0){
- algorithm = 1;
- document.getElementById("changealgobutton").innerHTML = "Brute Force";
- } else {
- algorithm = 0;
- fillSparse();
- document.getElementById("changealgobutton").innerHTML = "Ignore Non Changing Cells";
- }
- }
- </script>
- </head>
- <body onkeydown="keyPress(event)" onkeyup="keyUp(event)" onmouseup="mouseUp(event)">
- <div style="position:relative;width:100%;height:100%;cursor:default;overflow:hidden;background-color:#404040;" id="appContainer" onmousemove="mouseMove(event)">
- <canvas height="64" width="64" id="display" onmousedown="mouseDown(event)" style="width:512px;height:512px;position:absolute;font-size:32px;">This text displays if you have javascript disabled, or I guess if your browser doesn't support canvases. If it is the latter please reconsider your life choices/kill yourself. Otherwise enable js dude</canvas>
- <div style="position:absolute;top:0px;left:0px;background-color:rgba(0,0,0,0.75);border-bottom-right-radius:4px;" id="elementSelector">
- <div onmousedown="changeDrawType(-1)" id="sel-1" class="selMenuItem"><span style="background-color:#008888;padding:0px 4px 0px 4px;"> </span>R Select Tool</div>
- <span id="elements"></span>
- </div>
- <div style="position:absolute;top:0;right:0;padding:0px 6px 2px 0px;text-shadow:0px 0px 2px black;text-align:right;" onmousedown="mouseDown(event)">
- V0.2.0
- <!-- THE VERSION NUMBER IS HERE. Making it so it's easy to see in the source code. -->
- by blah,
- <a style="text-decoration:none;" href="http://www.ostracodfiles.com/logic/logicland.html">Inspired by Jack Eisenmann</a><br />
- World dimensions:
- <span class="number" id="worldXDisplay">64</span>x<span class="number" id="worldYDisplay">64</span><br />
- Selection dimensions:
- <span class="number" id="rectdisx">420</span>x<span class="number" id="rectdisy">69</span><br />
- Generation <span class="number" id="genNum">413</span>, <span class="number" id="gpsdisplay">612</span> generations/second
- </div>
- <div style="position:absolute;bottom:-1px;background-color:rgba(0,0,0,0.75);width:100%;line-height:20px;padding:2px 4px 2px 4px;">
- <span class="button" onclick="help.style.zIndex='0';help.style.opacity='1'">‽</span>
- | Zoom:
- <span class="button" onclick="changeZoom(zoom*2)">++</span>
- <span class="button" onclick="changeZoom(zoom+1)">+</span>
- <span id="zoomCounter" class="number">8</span>
- <span class="button" onclick="changeZoom(zoom-1)">-</span>
- <span class="button" onclick="changeZoom(Math.round(zoom/2))">--</span>
- | Δt:
- <span class="button" onclick="changeDT(speedDT*2)">++</span>
- <span class="button" onclick="changeDT(speedDT+1)">+</span>
- <span id="DTcounter" class="number">15ms (67fps)</span>
- <span class="button" onclick="changeDT(speedDT-1)">-</span>
- <span class="button" onclick="changeDT(Math.round(speedDT/2))">--</span>
- | generations/frame:
- <span class="button" onclick="changeSpeedMult(speedMult*2)">++</span>
- <span class="button" onclick="changeSpeedMult(speedMult+1)">+</span>
- <span id="multCounter" class="number">1</span>
- <span class="button" onclick="changeSpeedMult(speedMult-1)">-</span>
- <span class="button" onclick="changeSpeedMult(Math.round(speedMult/2))">--</span>
- | Pan camera:
- <span class="button" onclick="pan(0)">←</span>
- <span class="button" onclick="pan(1)">↑</span>
- <span class="button" onclick="pan(2)">→</span>
- <span class="button" onclick="pan(3)">↓</span>
- | <span class="button" onclick="pausePlay();" id="pauseDisplay">Pause</span>
- | Logic Land save codes:
- <span class="button" onclick="displaySaveCode()">get</span>
- <span class="button" onclick="setSaveCode(document.getElementById('RRFbox').value)">set</span>
- <input type="text" id="RRFbox" size="16" onfocus="this.select()" />
- | RLE save codes:
- <span class="button" onclick="getRLE(cellList,worldY,worldX)">get</span>
- <span class="button" onclick="setRLE(document.getElementById('RLEbox').value)">set</span>
- <textarea id="RLEbox" style="height:16px" onfocus="this.select()"></textarea>
- | <span class="button" onclick="if(!pauselock){tick()}">Simulate one frame</span> <!-- This has no-break spaces in the text so that part of the button doesn't be on a different line to the rest. That happened. Remember to use nbsps! -->
- | <span class="button" onclick="eorcf()" id="eorcDisplay">Expand</span>
- the world by
- <input type="text" id="expandAmount" size="4" value="16" onfocus="this.select()" />
- pixels on the
- <span class="button" onclick="dirf()" id="dirDisplay">Left</span>
- <span class="button" onclick="resize()">Go</span>
- | <span class="button" onclick="centerCamera()">Center Camera</span>
- | <span class="button" onclick="clearWorld();">Clear World</span>
- | Change Rule:
- <span class="button" onclick="setRule(document.getElementById('ruleBox').value);drawWorld()">set</span>
- <input type="text" id="ruleBox" size="10" value="B3/S23" onfocus="this.select()" />
- | Algorithm:
- <span class="button" id="changealgobutton" onclick="changeAlgorithm()">Brute Force</span>
- </div>
- <div id="HELP" style="
- width:100%;
- height:100%;
- z-index:-1;
- opacity:0;
- position:relative;
- background-color:rgba(0,0,0,0.75);
- overflow:auto;
- margin:auto;
- -webkit-user-select: text;
- -moz-user-select: text;
- -ms-user-select: text;">
- <span class="button" onclick="help.style.zIndex='-1';help.style.opacity='0'">Close</span>
- <p>Hello. I am blah. This is the help menu, though it's not actually a menu.</p>
- <!-- I've put comments in here marking sections -->
- <h1>Reasoning Realm</h1>
- <p>I declare this to be free software. I'm not sure how that works legally, but legalities aren't going to come into play here. Feel free to reverse engineer it, distribute it, modify it, distribute modifed versions of it, etc. I just request you give me credit.</p>
- <p>This is an implementation of cellular automata in javascript. It was originally simply an implementation of Jack Eisenmann's "<a href="http://www.ostracodfiles.com/logic/logicland.html">Logic Land</a>" cellular automaton, though I realised it would have more potential if it could simulate other rules. There is currently only support for Logic Land, lifelike CA, and Generations rules, though more will be added. It can import and export patterns in extended RLE format, in addition to the undocumented format that Logic Land used (though this is neglected, do not use it) with functions copy/pasted straight from Jack Eisenmann's code, with modifications to convert it into Reasoning Realm's internal format (a 2d array, with different values for cell types). "Reasoning Realm" is a play on the name "Logic Land", obviously; Intelligence Island could be another, so could Deduction Dimension or Smartness Sphere (those last two are courtesy of Dan). I've been working on this since 2016-03-03. The text in the menus is unselectable (in browsers that support my methods) (but this text can be selected) because the buttons are spans, and if you could select the text that would happen while you were clicking the buttons. Sorry if not being able to select the text frustrates you. The clipboard is initalised to something, so if you use the selection tool to paste, without first copying, you'll see its default content. The source code of this program is over 1000 lines.</p>
- <p>Credit goes to <span class="blah">BLAH (ME)</span> for being the most graceful, intelligent, all-seeing supreme being to have ever existed within or surrounding the omniverse and all other possible worlds, Jack Eisenmann for inspiration, and dan.</p>
- <!-- section -->
- <h2>Controls</h2>
- <p>
- left click and drag over the canvas with the mouse to draw the selected element<br />
- wasd/arrow keys: move camera<br />
- F: simulate one frame<br />
- I: zoom in linearly<br />
- O: zoom out linearly<br />
- [: zoom out exponentially<br />
- ]: zoom in exponentially<br />
- space: play/pause<br />
- E: erase selection<br />
- X: cut selection<br />
- C: copy selection<br />
- V: paste selection<br />
- M: mirror selection vertically<br />
- N: mirror selection horizontally<br />
- T: rotate selection<br />
- H: center camera<br />
- 0,1,2,3,4,5,6,7,8,9: select corresponding element<br />
- Q: same as 0<br />
- R: selection tool<br />
- -: Gate Output (the hyphen key, to the right of 0)<br />
- alt+f4: close help menu
- </p>
- <p>Pause lock. If you enable this, you will not be able to pause or unpause. useful iff you=dan <span class="button" onclick="pauseLock()" id="pauseLockDisplay">Lock</span></p>
- <!-- section -->
- <h2>Changelog</h2>
- <h3>0.2.0</h3>
- <p class="date">2016-07-03</p>
- <ul class="changelist">
- <li>New optimised algorithm. The code for simulating a frame has been almost entirely remade. You can choose to use the old algorithm, as it is still faster for some things.</li>
- <li>Added selection size indicator to top right</li>
- <li>Added hotkey H for center camera</li>
- <li>Pause lock now prevents simulating single steps (hotkey F and corresponding menu item)</li>
- <li>Changed background colour to #404040</li>
- <li>The app now autofits the browser window, and the 'resize app' option has been removed (to make the menu less cluttered with useless options; it still worked)</li>
- <li>Fixed bug where rectangle select tool would cause program halting errors if you resized to remove its origin, or pasted into a domain smaller than the clipboard.</li>
- <li>Added hotkey T to rotate selection, though glitchily</li>
- <li>Added generation counter and generations/second in the top right</li>
- <li>Added ++ and -- buttons for changing speed</li>
- <li>Added splashscreen, an RLE that gets loaded when RR starts</li>
- </ul>
- <!-- section -->
- <h2>Todo List</h2>
- <p>This is a list of easy and difficult goals. Some I will probably never bother to do. They are in no order whatsoever. This list is maintained poorly if at all.</p>
- <ul>
- <li>Fix the rotation.</li>
- <li>Add options for colour schemes.</li>
- <li>Add camera drag tool for panning the camera with the mouse.</li>
- <li>Make the 'close' button in the help menu follow you as you scroll down.</li>
- <li>Capture keyboard inputs for textboxes (and the help menu); right now the keyboard shortcuts are triggered when you type in a text box.</li>
- <li>Add support for unbounded grids</li>
- <li>Add support for other geometries, like cylinders, toruses, klein bottles, etc</li>
- <li>Combine both savecode boxes into a single box which can tell what format is being used</li>
- <li>Allow multiple worlds loaded at once</li>
- <li>Add undo functionality</li>
- <li>Bug: Changing the rule to one with n states causes errors which halt the program if there is a cell with state x>n</li>
- <li>Rewrite getRLE() as it is incredibly poorly put together and glitchy.</li>
- </ul>
- </div>
- </div>
- <script>
- //Script at the end of <body> to initialize the app.
- var c = document.getElementById("display");
- var canvas = c.getContext("2d");
- var crect = c.getBoundingClientRect(); //This has to do with getting the location of the mouse over the canvas. See this stackoverflow page: http://stackoverflow.com/questions/17130395/canvas-html5-real-mouse-position
- var id = canvas.createImageData(1,1); //It's worth reading this stackoverflow page for info on this variable: http://stackoverflow.com/questions/4899799/whats-the-best-way-to-set-a-single-pixel-in-an-html5-canvas
- var d = id.data;
- d[3] = 255; //d[3] is opacity, which should be full.
- var help = document.getElementById("HELP");
- var selMenu = document.getElementById("elements");
- setRule("logicland");
- drawWorld();
- changeDrawType(1); //We have to call this so the menu doesn't have no items selected. Having no items selected is not something the menu should ever be able to do
- setInterval("if(running){tickCount++; if(tickCount%speedDT == 0){tick()}}", 15); //The if statement is here, not in the function, so we can manually simulate frames. also the number is the dt in ms
- crect = c.getBoundingClientRect();
- appWidth = document.getElementById('appContainer').getBoundingClientRect().width;
- appHeight = document.getElementById('appContainer').getBoundingClientRect().height;
- setInterval("crect = c.getBoundingClientRect(); appWidth = document.getElementById('appContainer').getBoundingClientRect().width; appHeight = document.getElementById('appContainer').getBoundingClientRect().height", 5000); //It re-establishes that variable every 5 seconds because I'm too lazy to find out how to trigger an event when body is resized
- setInterval("continuousPan();", 50); //This function's name is self-explanatory; it checks to see if the wasd/><^V keys are being pressed and if so moves the camera accordingly when it is called.
- setInterval("document.getElementById('gpsdisplay').innerHTML = generation-gpscount; gpscount = generation;",1000);
- centerCamera();
- setRLE("#C This is the splash screen for Reasoning Realm. This is a comment! Yeah!!!\nx = 64, y = 64, rule = logicland\n4$7.4A2.4A2.3A3.3A2.3A2.4A2.2A.4A3.4A.BA$7.2A.2A.4A.5A.4A.5A.5A.2A.5A.5A.CA$7.2A.2A.2A3.2A.2A.2A3.2A.2A.2A.2A.2A.2A.2A.2A4.I$7.4A2.4A.2A.2A.3A2.2A.2A.2A.2A.2A.2A.2A.2A4.JA$7.2A.2A.4A.5A2.3A.2A.2A.2A.2A.2A.2A.2A.2A.2A2.I$7.2A.2A.2A3.2A.2A3.2A.2A.2A.2A.2A.2A.2A.2A.2A.2A.AJ$7.2A.2A.4A.2A.2A.4A.5A.2A.2A.2A.2A.2A.5A.I$7.2A.2A.4A.2A.2A.3A3.3A2.2A.2A.2A.2A.2A2.3A2.JA$7.DADADADADADADADADADADADADADADADADADADADADADADADAJ.I$17.4A2.4A2.3A2.2A4.7A8.IAJ$17.2A.2A.4A.5A.2A4.8A$17.2A.2A.2A3.2A.2A.2A4.2A.2A.2A$17.4A2.4A.2A.2A.2A4.2A.2A.2A$17.2A.2A.4A.5A.2A4.2A.2A.2A$17.2A.2A.2A3.2A.2A.2A4.2A.2A.2A$17.2A.2A.4A.2A.2A.5A.2A.2A.2A$17.2A.2A.4A.2A.2A.5A.2A.2A.2A2$23.J.J2.J4.2J5.J$23.J.J.J.J5.J3.J.J$23.J.J.J.J3.2J4.J.J$24.J3.J2.J.3J.J2.J4$20.2J2.J.J3.2J2.J2.3J.J.J$20.J.J.J.J3.J.J.J2.J.J.J.J$20.2J3.J4.2J2.J2.3J.3J$20.J.J2.J4.J.J.J2.J.J.J.J$20.2J3.J4.2J2.2J.J.J.J.J!");
- </script>
- </body>
- </html>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement