// rockPaperScissorsCells.js // examples // // Created by Ben Arnold on 7/16/14. // Copyright 2014 High Fidelity, Inc. // // This sample script creates a voxel wall that simulates the Rock Paper Scissors cellular // automata. http://www.gamedev.net/blog/844/entry-2249737-another-cellular-automaton-video/ // If multiple instances of this script are run, they will combine into a larger wall. // NOTE: You must run each instance one at a time. If they all start at once there are race conditions. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // var NUMBER_OF_CELLS_EACH_DIMENSION = 48; var NUMBER_OF_CELLS_REGION_EACH_DIMESION = 16; var REGIONS_EACH_DIMENSION = NUMBER_OF_CELLS_EACH_DIMENSION / NUMBER_OF_CELLS_REGION_EACH_DIMESION; var isLocal = false; var currentCells = []; var nextCells = []; var cornerPosition = {x: 100, y: 0, z: 0 } var position = {x: 0, y: 0, z: 0 }; var METER_LENGTH = 1; var cellScale = (NUMBER_OF_CELLS_EACH_DIMENSION * METER_LENGTH) / NUMBER_OF_CELLS_EACH_DIMENSION; var viewerPosition = {x: cornerPosition.x + (NUMBER_OF_CELLS_EACH_DIMENSION / 2) * cellScale, y: cornerPosition.y + (NUMBER_OF_CELLS_EACH_DIMENSION / 2) * cellScale, z: cornerPosition.z }; viewerPosition.z += 50; var yaw = 0; var orientation = Quat.fromPitchYawRollDegrees(0, yaw, 0); //Feel free to add new cell types here. It can be more than three. var cellTypes = []; cellTypes[0] = { r: 255, g: 0, b: 0 }; cellTypes[1] = { r: 0, g: 255, b: 0 }; cellTypes[2] = { r: 0, g:0, b: 255 }; cellTypes[3] = { r: 0, g: 255, b: 255 }; //Check for free region for AC var regionMarkerX = -1; var regionMarkerY = -1; var regionMarkerI = -1; var regionMarkerJ = -1; var regionMarkerColor = {r: 254, g: 0, b: 253}; function setRegionToColor(startX, startY, width, height, color) { for (var i = startY; i < startY + height; i++) { for (var j = startX; j < startX + width; j++) { currentCells[i][j] = { changed: true, type: color }; // put the same value in the nextCells array for first board draw nextCells[i][j] = { changed: true, type: color }; } } } function init() { for (var i = 0; i < REGIONS_EACH_DIMENSION; i++) { for (var j = 0; j < REGIONS_EACH_DIMENSION; j++) { var x = cornerPosition.x + (j) * cellScale; var y = cornerPosition.y + (i + NUMBER_OF_CELLS_EACH_DIMENSION) * cellScale; var z = cornerPosition.z; var voxel = Voxels.getVoxelAt(x, y, z, cellScale); if (voxel.x != x || voxel.y != y || voxel.z != z || voxel.s != cellScale || voxel.red != regionMarkerColor.r || voxel.green != regionMarkerColor.g || voxel.blue != regionMarkerColor.b) { regionMarkerX = x; regionMarkerY = y; regionMarkerI = i; regionMarkerJ = j; i = REGIONS_EACH_DIMENSION; //force quit loop break; } } } //Didnt find an open spot, end script if (regionMarkerX == -1) { Script.stop(); } position.x = cornerPosition.x + regionMarkerJ * NUMBER_OF_CELLS_REGION_EACH_DIMESION; position.y = cornerPosition.y + regionMarkerI * NUMBER_OF_CELLS_REGION_EACH_DIMESION; position.z = cornerPosition.z; Voxels.setVoxel(regionMarkerX, regionMarkerY, cornerPosition.z, cellScale, regionMarkerColor.r, regionMarkerColor.g, regionMarkerColor.b); for (var i = 0; i < NUMBER_OF_CELLS_REGION_EACH_DIMESION; i++) { // create the array to hold this row currentCells[i] = []; // create the array to hold this row in the nextCells array nextCells[i] = []; } var width = NUMBER_OF_CELLS_REGION_EACH_DIMESION / 2; setRegionToColor(0, 0, width, width, 0); setRegionToColor(0, width, width, width, 1); setRegionToColor(width, width, width, width, 2); setRegionToColor(width, 0, width, width, 3); } function updateCells() { var i = 0; var j = 0; var cell; var y = 0; var x = 0; for (i = 0; i < NUMBER_OF_CELLS_REGION_EACH_DIMESION; i++) { for (j = 0; j < NUMBER_OF_CELLS_REGION_EACH_DIMESION; j++) { cell = currentCells[i][j]; var r = Math.floor(Math.random() * 8); switch (r){ case 0: y = i - 1; x = j - 1; break; case 1: y = i; x = j-1; break; case 2: y = i + 1; x = j - 1; break; case 3: y = i + 1; x = j; break; case 4: y = i + 1; x = j + 1; break; case 5: y = i; x = j + 1; break; case 6: y = i - 1; x = j + 1; break; case 7: y = i - 1; x = j; break; default: continue; } //check the voxel grid instead of local array when on the edge if (x == -1 || x == NUMBER_OF_CELLS_REGION_EACH_DIMESION || y == -1 || y == NUMBER_OF_CELLS_REGION_EACH_DIMESION) { var voxel = Voxels.getVoxelAt(position.x + x * cellScale, position.y + y * cellScale, position.z, cellScale); var predatorCellType = ((cell.type + 1) % cellTypes.length); var predatorCellColor = cellTypes[predatorCellType]; if (voxel.red == predatorCellColor.r && voxel.green == predatorCellColor.g && voxel.blue == predatorCellColor.b) { nextCells[i][j].type = predatorCellType; nextCells[i][j].changed = true; } } else { if (currentCells[y][x].type == ((cell.type + 1) % cellTypes.length)) { nextCells[i][j].type = currentCells[y][x].type; nextCells[i][j].changed = true; } else { //indicate no update nextCells[i][j].changed = false; } } } } for (i = 0; i < NUMBER_OF_CELLS_REGION_EACH_DIMESION; i++) { for (j = 0; j < NUMBER_OF_CELLS_REGION_EACH_DIMESION; j++) { if (nextCells[i][j].changed == true) { // there has been a change to this cell, change the value in the currentCells array currentCells[i][j].type = nextCells[i][j].type; currentCells[i][j].changed = true; } } } } function sendNextCells() { for (var i = 0; i < NUMBER_OF_CELLS_REGION_EACH_DIMESION; i++) { for (var j = 0; j < NUMBER_OF_CELLS_REGION_EACH_DIMESION; j++) { if (nextCells[i][j].changed == true) { // there has been a change to the state of this cell, send it // find the x and y position for this voxel, z = 0 var x = j * cellScale; var y = i * cellScale; var type = nextCells[i][j].type; // queue a packet to add a voxel for the new cell Voxels.setVoxel(position.x + x, position.y + y, position.z, cellScale, cellTypes[type].r, cellTypes[type].g, cellTypes[type].b); } } } } var sentFirstBoard = false; var voxelViewerInit = false; var UPDATES_PER_SECOND = 6.0; var frameIndex = 1.0; var oldFrameIndex = 0; var framesToWait = UPDATES_PER_SECOND; function step(deltaTime) { if (isLocal == false) { if (voxelViewerInit == false) { VoxelViewer.setPosition(viewerPosition); VoxelViewer.setOrientation(orientation); voxelViewerInit = true; } VoxelViewer.queryOctree(); } frameIndex += deltaTime * UPDATES_PER_SECOND; if (Math.floor(frameIndex) == oldFrameIndex) { return; } oldFrameIndex++; if (frameIndex <= framesToWait) { return; } if (sentFirstBoard) { // we've already sent the first full board, perform a step in time updateCells(); } else { // this will be our first board send sentFirstBoard = true; init(); } if (isLocal == false) { VoxelViewer.queryOctree(); } sendNextCells(); } function scriptEnding() { Voxels.eraseVoxel(regionMarkerX, regionMarkerY, position.z, cellScale); } Script.scriptEnding.connect(scriptEnding); Script.update.connect(step); Voxels.setPacketsPerSecond(2000); // test for local... Menu.isOptionChecked("Voxels"); isLocal = true; // will only get here on local client