diff --git a/F b/F new file mode 100644 index 00000000..6a69f920 --- /dev/null +++ b/F @@ -0,0 +1 @@ +f diff --git a/README.md b/README.md index ee152a08..4e5265f6 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ Organism Cells are only found in organisms, and cannot exist on their own in the - Killer - Red, harms organisms in directly adjacent cells (besides itself). - Armor - Purple, negates the effects of killer cells. - Eye - Light purple with a slit, allows the organism to see and move intelligently. See further description below. +- Fin - Yellow, Allows creature to swim in water. ## Organisms Organisms are structures of cells that eat food, reproduce, and die. diff --git a/dist/css/style.css b/dist/css/style.css index 1714871e..a08c3a99 100644 --- a/dist/css/style.css +++ b/dist/css/style.css @@ -172,6 +172,9 @@ button:active{ #clear-walls { margin-top: 5px; } +#clear-water { + margin-top: 5px; +} #organism-options { display: none; } diff --git a/dist/index.html b/dist/index.html index 0166f539..26e226f6 100644 --- a/dist/index.html +++ b/dist/index.html @@ -23,8 +23,11 @@ + - + + +

Simulation Speed

@@ -78,11 +81,13 @@

Cell Types

+
+

Hover over each color to learn what it does. For a more in depth explanation of the simulation, view the @@ -110,6 +115,7 @@

Cell Types

+
@@ -126,6 +132,7 @@

Cell Types

Organism Details

Cell count:

Move Range:

+

Is Aquatic:

Mutation Rate:


@@ -143,6 +150,10 @@

Edit Organism

+
+ + +
@@ -156,6 +167,7 @@

Brain

+ @@ -269,6 +281,7 @@

Select a Challenge

+ diff --git a/src/Controllers/ControlModes.js b/src/Controllers/ControlModes.js index 882e5001..e618ce94 100644 --- a/src/Controllers/ControlModes.js +++ b/src/Controllers/ControlModes.js @@ -6,7 +6,8 @@ const Modes = { Select: 4, Edit: 5, Clone: 6, - Drag: 7 + Drag: 7, + WaterDrop: 8, } module.exports = Modes; \ No newline at end of file diff --git a/src/Controllers/ControlPanel.js b/src/Controllers/ControlPanel.js index 5ec9e548..d4e9fe69 100644 --- a/src/Controllers/ControlPanel.js +++ b/src/Controllers/ControlPanel.js @@ -288,6 +288,9 @@ class ControlPanel { case "wall-drop": self.setMode(Modes.WallDrop); break; + case "water-drop": + self.setMode(Modes.WaterDrop); + break; case "click-kill": self.setMode(Modes.ClickKill); break; @@ -323,6 +326,11 @@ class ControlPanel { if (confirm("Are you sure you want to clear all the walls?")) { this.engine.env.clearWalls(); } + }.bind(this)); + $('#clear-water').click( function() { + if (confirm("Are you sure you want to clear all the water?")) { + this.engine.env.clearWater(); + } }.bind(this)); $('#clear-editor').click( function() { this.engine.organism_editor.clear(); diff --git a/src/Controllers/EditorController.js b/src/Controllers/EditorController.js index f0c101e8..2d6f9c2b 100644 --- a/src/Controllers/EditorController.js +++ b/src/Controllers/EditorController.js @@ -44,10 +44,11 @@ class EditorController extends CanvasController{ } else if (this.right_click) this.env.removeCellFromOrg(this.mouse_c, this.mouse_r); - + this.new_species = true; this.setBrainPanelVisibility(); this.setMoveRangeVisibility(); + this.setAquaticVisibility() this.updateDetails(); } @@ -68,6 +69,9 @@ class EditorController extends CanvasController{ case "mover": self.edit_cell_type = CellStates.mover; break; + case "fin": + self.edit_cell_type = CellStates.fin; + break; case "killer": self.edit_cell_type = CellStates.killer; break; @@ -93,9 +97,9 @@ class EditorController extends CanvasController{ $('#move-range-edit').change ( function() { this.env.organism.move_range = parseInt($('#move-range-edit').val()); }.bind(this)); - - $('#mutation-rate-edit').change ( function() { - this.env.organism.mutability = parseInt($('#mutation-rate-edit').val()); + + $('#is-aquatic-edit').change(function() { + this.env.organism.is_aquatic = $('#is-aquatic-edit').is(":checked"); }.bind(this)); $('#observation-type-edit').change ( function() { this.setBrainEditorValues($('#observation-type-edit').val()); @@ -127,9 +131,10 @@ class EditorController extends CanvasController{ $('.cell-count').text("Cell count: "+org.anatomy.cells.length); $('#move-range').text("Move Range: "+org.move_range); + $('#is-aquatic').text("Is Aquatic: "+org.is_aquatic); + $('#mutation-rate').text("Mutation Rate: "+org.mutability); - - if (Hyperparams.useGlobalMutability) { + if (Hyperparams.useGlobalMutability) { $('#mutation-rate').css('display', 'none'); } else { @@ -154,6 +159,10 @@ class EditorController extends CanvasController{ $('#move-range-edit').val(org.move_range); } + if (this.setAquaticVisibility()){ + $('#is-aquatic-edit').val(org.is_aquatic); + } + $('#mutation-rate-edit').val(org.mutability); if (Hyperparams.useGlobalMutability) { $('#mutation-rate-cont').css('display', 'none'); @@ -208,6 +217,20 @@ class EditorController extends CanvasController{ return false; } + setAquaticVisibility() { + var org = this.env.organism; + + if (org.anatomy.has_fins) { + $('#is-aquatic-cont').css('display', 'block'); + $('#is-aquatic').css('display', 'block'); + return true; + } + org.is_aquatic = false + $('#is-aquatic-cont').css('display', 'none'); + $('#is-aquatic').css('display', 'none'); + return false; + } + setBrainEditorValues(name) { $('#observation-type-edit').val(name); var reaction = this.env.organism.brain.decisions[name]; diff --git a/src/Controllers/EnvironmentController.js b/src/Controllers/EnvironmentController.js index 73aa0fca..3ecef51a 100644 --- a/src/Controllers/EnvironmentController.js +++ b/src/Controllers/EnvironmentController.js @@ -99,6 +99,28 @@ class EnvironmentController extends CanvasController{ this.dropCellType(cell.col, cell.row, CellStates.empty, false); } break; + case Modes.WaterDrop: + if (left_click){ + this.env.water_map[cell.col][cell.row] = true + for (var loc of Neighbors.allSelf){ + var c=cell.col + loc[0]; + var r=cell.row + loc[1]; + this.env.water_map[c][r] = true + } + + this.dropCellType(cell.col, cell.row, CellStates.water, true); + + } + else if (right_click){ + this.env.water_map[cell.col][cell.row] = false + for (var loc of Neighbors.allSelf){ + var c=cell.col + loc[0]; + var r=cell.row + loc[1]; + this.env.water_map[c][r] = false + } + this.dropCellType(cell.col, cell.row, CellStates.empty, false); + } + break; case Modes.ClickKill: this.killNearOrganisms(); break; diff --git a/src/Environments/OrganismEditor.js b/src/Environments/OrganismEditor.js index b969774f..c5cb4b5b 100644 --- a/src/Environments/OrganismEditor.js +++ b/src/Environments/OrganismEditor.js @@ -59,6 +59,7 @@ class OrganismEditor extends Environment{ var prev_cell = this.organism.anatomy.getLocalCell(loc_c, loc_r) if (prev_cell != null) { if (this.organism.anatomy.removeCell(loc_c, loc_r)) { + this.changeCell(c, r, CellStates.empty, null); this.organism.species = new Species(this.organism.anatomy, null, 0); } diff --git a/src/Environments/WorldEnvironment.js b/src/Environments/WorldEnvironment.js index da7bb404..846f9c92 100644 --- a/src/Environments/WorldEnvironment.js +++ b/src/Environments/WorldEnvironment.js @@ -12,11 +12,13 @@ class WorldEnvironment extends Environment{ super(); this.renderer = new Renderer('env-canvas', 'env', cell_size); this.controller = new EnvironmentController(this, this.renderer.canvas); - var grid_rows = Math.ceil(this.renderer.height / cell_size); - var grid_cols = Math.ceil(this.renderer.width / cell_size); - this.grid_map = new GridMap(grid_cols, grid_rows, cell_size); + this.grid_rows = Math.ceil(this.renderer.height / cell_size); + this.grid_cols = Math.ceil(this.renderer.width / cell_size); + this.grid_map = new GridMap(this.grid_cols, this.grid_rows, cell_size); this.organisms = []; this.walls = []; + this.water = []; + this.water_map = []; this.total_mutability = 0; this.auto_reset = true; this.largest_cell_count = 0; @@ -69,6 +71,17 @@ class WorldEnvironment extends Environment{ } OriginOfLife() { + + for (var c = 0; c < this.grid_cols; c++) { + if(this.water_map[c] == null){ + this.water_map[c] = [] + } + for (var r = 0; r < this.grid_rows; r++) { + this.water_map[c][r] = false + + } + } + var center = this.grid_map.getCenter(); var org = new Organism(center[0], center[1], this); org.anatomy.addDefaultCell(CellStates.mouth, 0, 0); @@ -96,10 +109,19 @@ class WorldEnvironment extends Environment{ } changeCell(c, r, state, owner) { + if(state == CellStates.empty){ + if(this.water_map[c][r] == true){ + state = CellStates.water + } + } super.changeCell(c, r, state, owner); this.renderer.addToRender(this.grid_map.cellAt(c, r)); if(state == CellStates.wall) this.walls.push(this.grid_map.cellAt(c, r)); + if(state == CellStates.water){ + this.water.push(this.grid_map.cellAt(c, r)); + this.water_map[c][r] = true + } } clearWalls() { @@ -109,6 +131,15 @@ class WorldEnvironment extends Environment{ } } + + clearWater() { + for(var water of this.water){ + this.water_map[water.col][water.row] = false + if (this.grid_map.cellAt(water.col, water.row).state == CellStates.water) + this.changeCell(water.col, water.row, CellStates.empty, null); + } + } + clearOrganisms() { for (var org of this.organisms) org.die(); @@ -123,7 +154,7 @@ class WorldEnvironment extends Environment{ var c=Math.floor(Math.random() * this.grid_map.cols); var r=Math.floor(Math.random() * this.grid_map.rows); - if (this.grid_map.cellAt(c, r).state == CellStates.empty){ + if (this.grid_map.cellAt(c, r).state == CellStates.empty || this.grid_map.cellAt(c, r).state == CellStates.water){ this.changeCell(c, r, CellStates.food, null); } } diff --git a/src/Organism/Anatomy.js b/src/Organism/Anatomy.js index 8175024e..69932275 100644 --- a/src/Organism/Anatomy.js +++ b/src/Organism/Anatomy.js @@ -7,6 +7,7 @@ class Anatomy { this.cells = []; this.is_producer = false; this.is_mover = false; + this.has_fins = false; this.has_eyes = false; this.birth_distance = 4; } @@ -77,6 +78,7 @@ class Anatomy { checkTypeChange() { this.is_producer = false; this.is_mover = false; + this.has_fins = false; this.has_eyes = false; for (var cell of this.cells) { if (cell.state == CellStates.producer) @@ -85,6 +87,8 @@ class Anatomy { this.is_mover = true; if (cell.state == CellStates.eye) this.has_eyes = true; + if (cell.state == CellStates.fin) + this.has_fins = true; } } diff --git a/src/Organism/Cell/BodyCells/BodyCellFactory.js b/src/Organism/Cell/BodyCells/BodyCellFactory.js index 577f609f..b841c7e8 100644 --- a/src/Organism/Cell/BodyCells/BodyCellFactory.js +++ b/src/Organism/Cell/BodyCells/BodyCellFactory.js @@ -1,6 +1,7 @@ const MouthCell = require("./MouthCell"); const ProducerCell = require("./ProducerCell"); const MoverCell = require("./MoverCell"); +const FinCell = require("./FinCell"); const KillerCell = require("./KillerCell"); const ArmorCell = require("./ArmorCell"); const EyeCell = require("./EyeCell"); @@ -13,6 +14,7 @@ const BodyCellFactory = { type_map[CellStates.mouth.name] = MouthCell; type_map[CellStates.producer.name] = ProducerCell; type_map[CellStates.mover.name] = MoverCell; + type_map[CellStates.fin.name] = FinCell; type_map[CellStates.killer.name] = KillerCell; type_map[CellStates.armor.name] = ArmorCell; type_map[CellStates.eye.name] = EyeCell; diff --git a/src/Organism/Cell/BodyCells/FinCell.js b/src/Organism/Cell/BodyCells/FinCell.js new file mode 100644 index 00000000..ef2420b7 --- /dev/null +++ b/src/Organism/Cell/BodyCells/FinCell.js @@ -0,0 +1,11 @@ +const CellStates = require("../CellStates"); +const BodyCell = require("./BodyCell"); + +class FinCell extends BodyCell{ + constructor(org, loc_col, loc_row){ + super(CellStates.fin, org, loc_col, loc_row); + this.org.anatomy.has_fins = true; + } +} + +module.exports = FinCell; \ No newline at end of file diff --git a/src/Organism/Cell/BodyCells/ProducerCell.js b/src/Organism/Cell/BodyCells/ProducerCell.js index b6c4b3e4..9b306ba7 100644 --- a/src/Organism/Cell/BodyCells/ProducerCell.js +++ b/src/Organism/Cell/BodyCells/ProducerCell.js @@ -20,7 +20,7 @@ class ProducerCell extends BodyCell{ var loc_c=loc[0]; var loc_r=loc[1]; var cell = env.grid_map.cellAt(real_c+loc_c, real_r+loc_r); - if (cell != null && cell.state == CellStates.empty){ + if (cell != null && (cell.state == CellStates.empty || (cell.state == CellStates.water && this.org.anatomy.has_fins))){ env.changeCell(real_c+loc_c, real_r+loc_r, CellStates.food, null); return; } diff --git a/src/Organism/Cell/CellStates.js b/src/Organism/Cell/CellStates.js index 4a01aa76..a6764041 100644 --- a/src/Organism/Cell/CellStates.js +++ b/src/Organism/Cell/CellStates.js @@ -26,6 +26,13 @@ class Wall extends CellState { super('wall'); } } + +class Water extends CellState { + constructor() { + super('water'); + } +} + class Mouth extends CellState { constructor() { super('mouth'); @@ -41,6 +48,12 @@ class Mover extends CellState { super('mover'); } } + +class Fin extends CellState { + constructor() { + super('fin'); + } +} class Killer extends CellState { constructor() { super('killer'); @@ -78,14 +91,16 @@ const CellStates = { empty: new Empty(), food: new Food(), wall: new Wall(), + water: new Water(), mouth: new Mouth(), producer: new Producer(), mover: new Mover(), + fin: new Fin(), killer: new Killer(), armor: new Armor(), eye: new Eye(), defineLists() { - this.all = [this.empty, this.food, this.wall, this.mouth, this.producer, this.mover, this.killer, this.armor, this.eye] + this.all = [this.empty, this.food, this.wall, this.water, this.mouth, this.producer, this.mover, this.fin, this.killer, this.armor, this.eye] this.living = [this.mouth, this.producer, this.mover, this.killer, this.armor, this.eye]; }, getRandomName: function() { diff --git a/src/Organism/Organism.js b/src/Organism/Organism.js index 672c3020..95ae12f8 100644 --- a/src/Organism/Organism.js +++ b/src/Organism/Organism.js @@ -20,6 +20,7 @@ class Organism { this.can_rotate = Hyperparams.moversCanRotate; this.move_count = 0; this.move_range = 4; + this.is_aquatic = false; this.ignore_brain_for = 0; this.mutability = 5; this.damage = 0; @@ -31,6 +32,7 @@ class Organism { inherit(parent) { this.move_range = parent.move_range; + this.is_aquatic = parent.is_aquatic && parent.anatomy.has_fins; this.mutability = parent.mutability; this.species = parent.species; // this.birth_distance = parent.birth_distance; @@ -80,9 +82,21 @@ class Organism { org.mutability = 1; } } + + if(!org.anatomy.has_fins){ + org.is_aquatic = false + } + + var mutated = false; if (Math.random() * 100 <= prob) { - if (org.anatomy.is_mover && Math.random() * 100 <= 10) { + if (org.anatomy.has_fins && Math.random() * 100 <= 5) { + if(Math.random() * 100 < 60){ + org.is_aquatic = true + }else{ + org.is_aquatic = false + } + }else if (org.anatomy.is_mover && Math.random() * 100 <= 10) { if (org.anatomy.has_eyes) { org.brain.mutate(); } @@ -234,7 +248,7 @@ class Organism { } isPassableCell(cell, parent){ - return cell != null && (cell.state == CellStates.empty || cell.owner == this || cell.owner == parent || cell.state == CellStates.food); + return cell != null && ((cell.state == CellStates.empty && !this.is_aquatic) || cell.owner == this || cell.owner == parent || (this.anatomy.has_fins && cell.state == CellStates.water) || cell.state == CellStates.food); } isClear(col, row, rotation=this.rotation, ignore_armor=false) { @@ -243,8 +257,11 @@ class Organism { if (cell==null) { return false; } + + //console.log(this) + // console.log(cell.owner == this) - if (cell.owner==this || cell.state==CellStates.empty || (!Hyperparams.foodBlocksReproduction && cell.state==CellStates.food) || (ignore_armor && loccell.state==CellStates.armor && cell.state==CellStates.food)){ + if (cell.owner==this || (this.anatomy.has_fins && cell.state == CellStates.water) || (cell.state == CellStates.empty && !this.is_aquatic) || (!Hyperparams.foodBlocksReproduction && cell.state==CellStates.food) || (ignore_armor && loccell.state==CellStates.armor && cell.state==CellStates.food)){ continue; } return false; diff --git a/src/Organism/Perception/Brain.js b/src/Organism/Perception/Brain.js index e52d8a31..19d88e48 100644 --- a/src/Organism/Perception/Brain.js +++ b/src/Organism/Perception/Brain.js @@ -24,9 +24,11 @@ class Brain { this.decisions[CellStates.empty.name] = Decision.neutral; this.decisions[CellStates.food.name] = Decision.chase; this.decisions[CellStates.wall.name] = Decision.neutral; + this.decisions[CellStates.water.name] = Decision.neutral; this.decisions[CellStates.mouth.name] = Decision.neutral; this.decisions[CellStates.producer.name] = Decision.neutral; this.decisions[CellStates.mover.name] = Decision.neutral; + this.decisions[CellStates.fin.name] = Decision.neutral; this.decisions[CellStates.killer.name] = Decision.retreat; this.decisions[CellStates.armor.name] = Decision.neutral; this.decisions[CellStates.eye.name] = Decision.neutral; @@ -37,6 +39,7 @@ class Brain { this.decisions[CellStates.mouth.name] = Decision.getRandom(); this.decisions[CellStates.producer.name] = Decision.getRandom(); this.decisions[CellStates.mover.name] = Decision.getRandom(); + this.decisions[CellStates.fin.name] = Decision.getRandom(); this.decisions[CellStates.armor.name] = Decision.getRandom(); this.decisions[CellStates.eye.name] = Decision.getRandom(); } diff --git a/src/Rendering/ColorScheme.js b/src/Rendering/ColorScheme.js index 30ce7f90..b61af99c 100644 --- a/src/Rendering/ColorScheme.js +++ b/src/Rendering/ColorScheme.js @@ -4,9 +4,11 @@ var color_scheme = { "empty":"#0E1318", "food":"#2F7AB7", "wall":"gray", + "water":"#0a47a3", "mouth":"#DEB14D", "producer":"#15DE59", "mover":"#60D4FF", + "fin":"#d9d029", "killer":"#F82380", "armor":"#7230DB", "eye":"#B6C1EA",