Minimax in Javascript not working properly - javascript

As a practice project I made a Tic-Tac-Toe game on JSFiddle (because there aren't enough already, right?) and I progressed into adding an unbeatable AI. For the most part it works, but there are some combinations (e.g. setting X into fields 5, 9, 3 or into fields 3, 7, 9) that lead to the computer not calculating the optimal move properly.
The project on JSFiddle: https://jsfiddle.net/jd8x0vjz/
And the relevant function starting at line 63:
function evaluateMove(move, player, depth) {
var gameStatus = evaluateGameStatus(move); //get status of current board
if (gameStatus < 2 && player)
return -1; //if human won, return -1
if (gameStatus < 2 && !player)
return 1; //if human lost, return 1
var returnValue = 0 //value to be returned later
for (var z = 0; z < 3; z++) { //loop for row
for (var s = 0; s < 3; s++) { //loop for column
if (move[z][s]) //if current slot has an x or o,
continue; //skip it
var nextMove = cloneGameStatus(move); //create temporary array with base of current grid
nextMove[z][s] = !player ? "x" : "o"; //assign first free field the appropriate symbol
var value = evaluateMove(nextMove, !player, depth+1); //recursion but with switched player, to add the correct icon afterwards
if ((value > returnValue) && player)
returnValue = value;
if ((value < returnValue) && !player)
returnValue = value;
}
}
return returnValue; //return value of current simulation
}
I think the last two if-clauses are causing these problems, since the computer does calculate the proper values (as observable in the debugger), but they're sometimes overwritten, but I am not sure if this really is the root of the problem. Any help or tips would be appreciated!
EDIT: Problem solved! Look for my answer below in case it is not the first one.

I can't say for sure this is the source of the problem but there is most definitely a bug in your code that will produce strange results. The line:
var returnValue = 0 //value to be returned later
is incorrect. Aside from the fact that you are missing a semi-colon, the proper code should be:
var returnValue = -1;
if(!player){
returnValue = 1;
}
You want the default value for the maximum player to be negative so that he takes the best move, and for the minimizing player positive so he takes the worst move. The way you did it, if the maximizing player was faced only with options that valued -1, since -1 is less than 0, and returnValue was initialized to 0, 0 would be returned although the correct value to be returned is -1.

The idea of the default value of returnValue being wrong definitely sent me down the right path; it didn't make everything magically work (would've been too nice if it did), but it did give me the right nudge. Since we don't want to return any value if nothing is calculated, I adjusted the evaluateMove function as following:
function evaluateMove(move, player, depth) {
var gameStatus = evaluateGameStatus(move); //get status of current board
if (gameStatus != 2)
return gameStatus; //if the game is not running anymore, return result
var returnValue; //value to be returned later
for (var z = 0; z < 3; z++) { //loop for row
for (var s = 0; s < 3; s++) { //loop for column
if (move[z][s]) //if current slot has an x or o,
continue; //skip it
var nextMove = cloneGameStatus(move); //create temporary array with base of current grid
nextMove[z][s] = !player ? "x" : "o"; //assign first free field the appropriate symbol
var value = evaluateMove(nextMove, !player, depth+1); //recursion but with switched player, to add the correct icon afterwards
if ((value > returnValue || returnValue == null) && player)
returnValue = value;
if ((value < returnValue || returnValue == null) && !player)
returnValue = value;
}
}
return returnValue; //return value of current simulation
}
Now the default is null and as such should not throw the calculations off. What it did throw off however was the first block of checks, so I adjusted it to simply return the current status should the game have ended, instead of doing any elaborate checks. However that threw off the results because I'm using inversed default values in the two methods, so I had to adjust evaluateGameStatus too. Now if the human won it returns -1 instead of 1, and if the computer won it returns 1 instead of -1:
function evaluateGameStatus(gameStatus) { //a clusterfuck of winning combinations
if(
X Checks
)
return -1; //there's a successful combination of x's
else if(
O Checks
)
return 1; //there's a successful combination of o's
else {
for (var z = 0; z < 3; z++) {
for (var s = 0; s < 3; s++) {
if (!gameStatus[z][s])
return 2; //if there is an empty field neither has won, continue playing
}
}
return 0; //there's no successful combination and max moves have been reached. it's a draw
}
}
I had to do the same adjustmends for the checkGameEnd function, obviously.
You'll notice that I changed the check for draws too. That is because, for some reason, the old check for count == maxMoves did not work anymore, so I changed to a loop which simply checks whether there is any empty field at all, and to return 2 if there is, and 0 if there is not (it returns 0 here because at this point it has run through all the checks: X hasn't won, O hasn't won, and there are no open slots left, so the game must be a draw).
Working project can now be found here:
https://jsfiddle.net/h5zwzkm7/

Related

Search in Rotated Sorted Array( with algorithm )

I am trying to search for a target in rotated sorted ascending array in O(logn) time
For example:
Example 1:
Input: nums = [4,5,6,7,0,1,2], target = 0
Output: 4
Example 2:
Input: nums = [4,5,6,7,0,1,2], target = 3
Output: -1
My idea is that, in the rotated array, the whole sequence is not said to be sorted anymore.
But I found that if I give a cut in the middle, either the first half or second half is still sorted.
So, I pick the middle one and compare the values on both ends to check which part is still sorted. Then I check if the target is within the sorted range. And I am doing it recursively
Here is my code:
var search = function(nums, target) {
let start = 0
let end = nums.lenght -1;
let mid = 0;
while (start<=end){
mid = (start + end) / 2
if (target == nums[mid]){
return mid
}
else{
if (nums[mid]>nums[start] && nums[start]<taget<nums[mid]){
end = mid;
}else {
start = mid
}
}
}
return -1;
};
But I still got an error for such input [4,5,6,7,0,1,2] , 0 ; but output -1 ; I dont get why algorithm doesnt work and see the lacking. Can anyone see my faults?
----- second update --- corrected misspelled and condition sytax error
var search = function(nums, target) {
let start = 0
let end = nums.length -1;
let mid = 0;
while (start<=end){
mid = (start + end) / 2
if (target == nums[mid]){
return mid
}
else{
if (nums[mid]>nums[start]){ //it means it is ascending and sorted...
if(nums[start]<target && target<nums[mid]){ // if target is within the range, then it could only be possible to go first half
end = mid;
}
// it means the second half is sorted one otherwise
}else {
start = mid
}
}
}
return -1;
};
This passed the first case;
[2,5,6,0,0,1,2]
0
but got time exceeded on this
[2,5,6,0,0,1,2], target = -1
------ third edit , never expected it is so hard; I come up with 3 case to check... but not finished yet. I dont know which part went. wrong
var search = function(nums, target) {
let start = 0
let end = nums.length -1;
let mid = 0;
while (start<=end){
mid = (start + end) / 2
if (target == nums[mid]){
return mid
}
else{
//case1 + (sorted first half) +(sorted second half)
if (nums[mid]>nums[start] && num[mid]<nums[end]){
if(nums[start]<target && target<nums[mid]){
end = mid;
}else{
start=mid;
}
}
//case2 + - (sorted first half) +(unsorted second half)
else if (nums[mid]>nums[start] && num[mid]>nums[end]){
if(nums[start]<target && target<nums[mid]){
end = mid;
}else{
start = mid
}
}
//case3 - + (unsorted first half) +(sorted second half)
else {
if(nums[end]<target && target>nums[mid]){
start = mid;
}else{
end = mid
}
}
}
return -1;
};
but I dont have any line 75; my line goes to til line 47 only
-------------forth edit
I looked again the pattern and come up with a clearer observation. This passed one of the case. but got Time Limit Exceeded in this case:
[2,5,6,0,0,1,2]
3
var search = function(nums, target) {
let left = 0
let right = nums.length -1;
let mid = 0;
while (left<=right){
mid = (left + right) / 2
if (target == nums[mid]){
return mid
}
else{
if (nums[mid] < nums[right]){
if(nums[mid]<target && target<nums[right]){
left = mid;
}else{
right=mid;
}
}else{
if(nums[mid]>target && target<nums[right]){
left = mid;
}else{
right=mid;
}
}
}
}
return -1;
};
Ok, you have corrected the misspelled and syntax errors, let's think about the cases you could encounter :
The list is in order.
The list is rotated.
If the list is in order, you can apply the classic algorithm. If the list is rotated, there is one point at which the number goes from highest to lowest. As this point is unique (think about why this is always true) it can be only in one half of the list, meaning one is in order, the other is a rotated list. (It could also be in between the 2 halves, meaning both of them are in order, but this is an easy case).
Thus you need to split the list in half, and determine in which half the target could be (could the target be in both halves ?) : to do that, first check which half is sorted, then check if the targer could be in that half (by using the first and last value). If it can't, it's in the rotated half.
Then, if the target is in the sorted half, you continue with the classic algorithm, else you search the target in the rotated half, which is like searching the target in a rotated list.
--Old answer for history--
Not sure if it's that, but you misspelled nums.length in the setup, which then set end to NaN (not a number), and thus the while loop is never entered(NaN comparaisons never hold true), and you return -1.
Also, there are multiples errors on line if (nums[mid]>nums[start] && nums[start]<taget<nums[mid]){ :
target is misspelled
You can't compare A < x < B in one go, you need to do A < x && x < B
If the target is in the first half but it's not sorted (mid < x < start), you will search in the second half. You probably need 4 different cases there.

JS HackerRank Jesse and Cookies - Heap problem times out

I am trying to solve the Hackerrank problem Jesse and Cookies:
Jesse loves cookies and wants the sweetness of some cookies to be greater than value 𝑘. To do this, two cookies with the least sweetness are repeatedly mixed. This creates a special combined cookie with:
sweetness = (1 × Least sweet cookie + 2 × 2nd least sweet cookie).
This occurs until all the cookies have a sweetness ≥ 𝑘.
Given the sweetness of a number of cookies, determine the minimum number of operations required. If it is not possible, return −1.
Example
k = 9
A = [2,7,3,6,4,6]
The smallest values are 2, 3.
Remove them then return 2 + 2 × 3 = 8 to the array. Now A = [8,7,6,4,6].
Remove 4, 6 and return 4 + 2 × 6 = 16 to the array. Now A = [16,8,7,6].
Remove 6, 7, return 6 + 2 × 7 = 20 and A = [20,16,8,7].
Finally, remove 8, 7 and return 7 + 2 × 8 = 23 to A. Now A = [23,20,16].
All values are ≥ 𝑘 = 9 so the process stops after 4 iterations. Return 4.
I couldn't find a JavaScript solution or a hint for this problem. My code seems to be working, except that it times out for a large array (input size > 1 million).
Is there a way to make my code more efficient? I think the time complexity is between linear and O(n log n).
My Code:
function cookies(k, A) {
A.sort((a,b)=>a-b)
let ops = 0;
while (A[0] < k && A.length > 1) {
ops++;
let calc = (A[0] * 1) + (A[1] * 2);
A.splice(0, 2);
let inserted = false
if (A.length === 0) { // when the array is empty after splice
A.push(calc);
} else {
for (var i = 0; i < A.length && !inserted; i++) {
if (A[A.length - 1] < calc) {
A.push(calc)
inserted = true
} else if (A[i] >= calc) {
A.splice(i, 0, calc);
inserted = true
}
}
}
}
if (A[0] < k) {
ops = -1;
}
return ops;
}
It is indeed a problem that can be solved efficiently with a heap. As JavaScript has no native heap, just implement your own.
You should also cope with inputs that are huge, but where most values are greater than k. Those should not have to be part of the heap -- it would just make heap operations unnecessarily slower. Also, when cookies are augmented, they only need to enter back into the heap when they are not yet good enough.
Special care needs to be taken when the heap ends up with just one value (less than k). In that case it needs to be checked whether any good cookies were created (and thus did not end up in the heap). If so, then with one more operation the solution has been found. But if not, it means there is no solution and -1 should be returned.
Here is an implementation in JavaScript:
/* MinHeap implementation without payload. */
const MinHeap = {
/* siftDown:
* The node at the given index of the given heap is sifted down in its subtree
* until it does not have a child with a lesser value.
*/
siftDown(arr, i=0, value=arr[i]) {
if (i >= arr.length) return;
while (true) {
// Choose the child with the least value
let j = i*2+1;
if (j+1 < arr.length && arr[j] > arr[j+1]) j++;
// If no child has lesser value, then we've found the spot!
if (j >= arr.length || value <= arr[j]) break;
// Move the selected child value one level up...
arr[i] = arr[j];
// ...and consider the child slot for putting our sifted value
i = j;
}
arr[i] = value; // Place the sifted value at the found spot
},
/* heapify:
* The given array is reordered in-place so that it becomes a valid heap.
* Elements in the given array must have a [0] property (e.g. arrays). That [0] value
* serves as the key to establish the heap order. The rest of such an element is just payload.
* It also returns the heap.
*/
heapify(arr) {
// Establish heap with an incremental, bottom-up process
for (let i = arr.length>>1; i--; ) this.siftDown(arr, i);
return arr;
},
/* pop:
* Extracts the root of the given heap, and returns it (the subarray).
* Returns undefined if the heap is empty
*/
pop(arr) {
// Pop the last leaf from the given heap, and exchange it with its root
return this.exchange(arr, arr.pop());
},
/* exchange:
* Replaces the root node of the given heap with the given node, and returns the previous root.
* Returns the given node if the heap is empty.
* This is similar to a call of pop and push, but is more efficient.
*/
exchange(arr, value) {
if (!arr.length) return value;
// Get the root node, so to return it later
let oldValue = arr[0];
// Inject the replacing node using the sift-down process
this.siftDown(arr, 0, value);
return oldValue;
},
/* push:
* Inserts the given node into the given heap. It returns the heap.
*/
push(arr, value) {
// First assume the insertion spot is at the very end (as a leaf)
let i = arr.length;
let j;
// Then follow the path to the root, moving values down for as long as they
// are greater than the value to be inserted
while ((j = (i-1)>>1) >= 0 && value < arr[j]) {
arr[i] = arr[j];
i = j;
}
// Found the insertion spot
arr[i] = value;
return arr;
}
};
function cookies(k, arr) {
// Remove values that are already OK so to keep heap size minimal
const heap = arr.filter(val => val < k);
let greaterPresent = heap.length < arr.length; // Mark whether there is a good cookie
MinHeap.heapify(heap);
let result = 0;
while (heap.length > 1) {
const newValue = MinHeap.pop(heap) + MinHeap.pop(heap) * 2;
// Only push result back to heap if it still is not great enough
if (newValue < k) MinHeap.push(heap, newValue);
else greaterPresent = true; // Otherwise just mark that we have a good cookie
result++;
}
// If not good cookies were created, then return -1
// Otherwise, if there is still 1 element in the heap, add 1
return greaterPresent ? result + heap.length : -1;
}
// Example run
console.log(cookies(9, [2,7,3,6,4,6])); // 4
I solved it using java. You may adapt to Javascript.
This code does not require using a heap. It just work on the same array passed. Passed all tests for me.
static int cookies(int k, int[] arr) {
/*
* Write your code here.
*/
Arrays.sort(arr);
int i = 0,
c = arr.length,
i0 = 0,
c0 = 0,
op = 0;
while( (arr[i]<k || arr[i0]<k) && (c0-i0 + c-i)>1 ) {
int s1 = i0==c0 || arr[i]<=arr[i0] ? arr[i++] : arr[i0++],
s2 = i0==c0 || (i!=c && arr[i]<=arr[i0]) ? arr[i++] : arr[i0++];
arr[c0++] = s1 + 2*s2;
op++;
if( i==c ) {
i = i0;
c = c0;
c0 = i0;
}
}
return c-i>1 || arr[i]>=k ? op : -1;
}
First of all sort array.
For newly calculated values, store them in the array[i0-c0] range, this new array does not require sorting, because it is already sorted.
When array[i-c] reaches(i==c: true) end, forget it, and work on arr[i0-c0].

Small Straight (Yahtzee) Algorithm

I have created a working javascript function to check an array of 5 numbers for a small straight, in a Yahtzee game I'm making. I've tested it to no end and I'm confident it works 100% of the time, but it is also probably the worst algorithm of all time in terms of being efficient. Here is what it looks like:
function calcSmstraight() {
var sum = 0;
var r = new Array();
var r2 = new Array();
var counter = 0;
var temp;
var bool = false;
var bool2 = false;
r[0] = document.getElementById('setKeep1').value;
r[1] = document.getElementById('setKeep2').value;
r[2] = document.getElementById('setKeep3').value;
r[3] = document.getElementById('setKeep4').value;
r[4] = document.getElementById('setKeep5').value;
// Move non-duplicates to new array
r2[0] = r[0];
for(var i=0; i<r.length; i++) {
for(var j=0; j<r2.length; j++) {
if(r[i] == r2[j]) {
bool2 = true; // Already in new list
}
}
// Add to new list if not already in it
if(!bool2) {
r2.push(r[i]);
}
bool2 = false;
}
// Make sure list has at least 4 different numbers
if(r2.length >= 4) {
// Sort dice from least to greatest
while(counter < r2.length) {
if(r2[counter] > r2[counter+1]) {
temp = r2[counter];
r2[counter] = r2[counter+1];
r2[counter+1] = temp;
counter = 0;
} else {
counter++;
}
}
// Check if the dice are in order
if(((r2[0] == (r2[1]-1)) && (r2[1] == (r2[2]-1)) && (r2[2] == (r2[3]-1)))
|| ((r2[1] == (r2[2]-1)) && (r2[2] == (r2[3]-1)) && (r2[3] == (r2[4]-1)))) {
bool = true;
}
}
if(bool) {
// If small straight give 30 points
sum = 30;
}
return sum;
}
My strategy is to:
1) Remove duplicates by adding numbers to a new array as they occur
2) Make sure the new array is at least 4 in length (4 different numbers)
3) Sort the array from least to greatest
4) Check if the first 4 OR last 4 (if 5 in length) numbers are in order
My question:
Does anyone know a way that I can improve this method? It seems ridiculously terrible to me but I can't think of a better way to do this and it at least works.
Given that you're implementing a Yahtzee game you presumably need to test for other patterns beyond just small straights, so it would be better to create the array of values before calling the function so that you can use them in all tests, rather than getting the values from the DOM elements inside the small straight test.
Anyway, here's the first way that came to my mind to test for a small straight within an array representing the values of five six-sided dice:
// assume r is an array with the values from the dice
r.sort();
if (/1234|2345|3456/.test(r.join("").replace(/(.)\1/,"$1") {
// is a small straight
}
Note that you can sort an array of numbers using this code:
r2.sort(function(a,b){return a-b;});
...but in your case the values in the array are strings because they came from the .value attribute of DOM elements, so a default string sort will work with r2.sort(). Either way you don't need your own sort routine, because JavaScript provides one.
EDIT: If you assume that you can just put the five values as a string as above you can implement tests for all possible combinations as a big if/else like this:
r.sort();
r = r.join("");
if (/(.)\1{4}/.test(r)) {
alert("Five of a Kind");
} else if (/(.)\1{3}/.test(r)) {
alert("Four of a Kind");
} else if (/(.)\1{2}(.)\2|(.)\3(.)\4{2}/.test(r)) {
alert("Full House");
} else if (/(.)\1{2}/.test(r)) {
alert("Three of a Kind");
} else if (/1234|2345|3456/.test( r.replace(/(.)\1/,"$1") ) {
alert("Small Straight");
} // etc.
Demo: http://jsfiddle.net/4Qzfw/
Why don't you just have a six-element array of booleans indicating whether a number is present, then check 1-4, 2-5, and 3-6 for being all true? In pseudocode:
numFlags = array(6);
foreach(dice)
numFlags[die.value-1] = true;
if(numFlags[0] && numFlags[1] && numFlags[2] && numFlags[3]) return true
//Repeat for 1-4 and 2-5
return false
This wouldn't be a useful algorithm if you were using million-sided dice, but for six-siders there are only three possible small straights to check for, so it's simple and straightforward.
I do not play Yahtzee, but I do play cards, and it would appear the algorithm might be similar. This routine, written in ActionScript (my JavaScript is a bit rusty) has been compiled but not tested. It should accept 5 cards for input, and return a message for either straights greater than 3 cards or pairs or higher.
private function checkCards(card1:int,card2:int,card3:int,card4:int,card5:int):String
{
// Assumes that the 5 cards have a value between 0-12 (Ace-King)
//The key to the routine is using the card values as pointers into an array of possible card values.
var aryCardValues:Array = new Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
aryCardValues[card1] += 1;
aryCardValues[card1] += 1;
aryCardValues[card1] += 1;
aryCardValues[card1] += 1;
aryCardValues[card1] += 1;
var aryCardNames:Array = new Array("Ace", "2", "3", "4", "5", "6", "7", "8", "9", "10", "Jack", "Queen", "King");
var strOutMessage:String;
var intCardCount:int = 0;
var strSeperator:String;
var strHighCard:String;
for (var i:int = 0;i < aryCardValues.length;i++)
{
//Check for runs of three of a kind or greater.
if (aryCardValues[i] >= 2)
{
strOutMessage = strOutMessage + strSeperator + i + "-" + aryCardNames[i] + "s";
strSeperator = " & ";
}
//Check for values in a straight.
if (aryCardValues[i] > 0)
{
intCardCount++;
if (intCardCount > 3)strHighCard = aryCardNames[i];
}
else
{
if (intCardCount < 3)intCardCount = 0;
}
}
if (intCardCount > 3) strOutMessage = intCardCount + " run " + strHighCard + " High."
return strOutMessage;
}
It may not be as concise as the regular expressions used above, but it might be more readable and easily modified. One change that could be made is to pass in an array of cards rather than discrete variables for each card.

Javascript challenge - which basket contains the last apple?

I'm presented with the following challenge question:
There are a circle of 100 baskets in a room; the baskets are numbered
in sequence from 1 to 100 and each basket contains one apple.
Eventually, the apple in basket 1 will be removed but the apple in
basket 2 will be skipped. Then the apple in basket 3 will be removed.
This will continue (moving around the circle, removing an apple from a
basket, skipping the next) until only one apple in a basket remains.
Write some code to determine in which basket the remaining apple is
in.
I concluded that basket 100 will contain the last apple and here's my code:
var allApples = [];
var apples = [];
var j = 0;
var max = 100;
var o ='';
while (j < max) {
o += ++j;
allApples.push(j);
}
var apples = allApples.filter(function(val) {
return 0 == val % 2;
});
while (apples.length > 1) {
for (i = 0; i < apples.length; i += 2) {
apples.splice(i, 1);
}
}
console.log(apples);
My question is: did I do this correctly? What concerns me is the description of "a circle" of baskets. I'm not sure this is relevant at all to how I code my solution. And would the basket in which the remaining apple reside be one that would otherwise be skipped?
I hope someone can let me know if I answered this correctly, answered it partially correct or my answer is entirely wrong. Thanks for the help.
So, ... I got WAY too into this question :)
I broke out the input/output of my last answer and that revealed a pretty simple pattern.
Basically, if the total number of items is a power of 2, then it will be the last item. An additional item after that will make the second item the last item. Each additional item after that will increase the last item by 2, until you reach another item count that is again divisible by a power of 2. Rinse and repeat.
Still not a one-liner, but will be much faster than my previous answer. This will not work for 1 item.
var items = 100;
function greatestPowDivisor(n, p) {
var i = 1;
while(n - Math.pow(p, i) > 0) {
i++;
}
return Math.pow(p, (i - 1));
}
var d = greatestPowDivisor(items, 2)
var last_item = (items - d) * 2;
I believe Colin DeClue is right that there is a single statement that will solve this pattern. I would be really interested to know that answer.
Here is my brute force solution. Instead of moving items ("apples") from their original container ("basket") into a discard pile, I am simply changing the container values from true or false to indicate that an item is no longer present.
var items = 100;
var containers = [];
// Just building the array of containers
for(i=0; i<items; i++) {
containers.push(true);
}
// count all containers with value of true
function countItemsLeft(containers) {
total = 0;
for(i=0; i<containers.length; i++) {
if(containers[i]) {
total++;
}
}
return total;
}
// what is the index of the first container
// with a value of true - hopefully there's only one
function getLastItem(containers) {
for(i=0; i<containers.length; i++) {
if(containers[i]) {
return(i);
}
}
// shouldn't get here if the while loop did it's job
return false;
}
var skip = false;
// loop through the items,
// setting every other to false,
// until there is only 1 left
while(countItemsLeft(containers) > 1) {
for(i=0; i<containers.length; i++) {
if(containers[i]) {
if(skip) {
skip = false;
} else {
containers[i] = false;
skip = true;
}
}
}
}
// what's the last item? add one to account for 0 index
// to get a human readable answer
var last_item = getLastItem(containers) + 1;
Needs error checking, etc... but it should get the job done assuming items is an integer.

Cellular automata implemented in JavaScript and HTML5 Canvas

I implemented a Conway's Game of Life in JavaScript but I'm not seeing the same patterns such as Gosper's Glider Gun. I seed the grid the ways it's depicted in the Wikipedia article but, the gun never happens.
Will someone look at my code and see if there's anything wrong with it, any suggestions to the implementation?
https://github.com/DiegoSalazar/ConwaysGameOfLife
You are not updating all of the cells simultaneously, rather sequentially. A cell that is born in the first generation will not appear alive to the calculation of other cells of the first generation (it still counts as dead).
Create a new property called willBeAlive and use that to hold the cell's new calculated alive state. Once all the calculations for that generation are done, set each cell's alive property to its willBeAlive property and redraw.
Here are the changes:
Automaton.prototype.update = function() {
for (var x = 0; x < this.w; x++) {
for (var y = 0; y < this.h; y++) {
this.grid[x][y].killYourselfMaybe();
}
}
// set the alive property to willBeAlive
for (var x = 0; x < this.w; x++) {
for (var y = 0; y < this.h; y++) {
this.grid[x][y].alive = this.grid[x][y].willBeAlive;
}
}
}
Cell.prototype.killYourselfMaybe = function(grid) {
var num = this.numLiveNeighbors();
this.willBeAlive = false;
if (this.alive) {
// 1. Any live cell with fewer than two live neighbours dies, as if caused by under-population.
if (num < 2) this.willBeAlive = false;
// 2. Any live cell with two or three live neighbours lives on to the next generation.
if (num == 2 || num == 3) { this.willBeAlive = true}
// 3. Any live cell with more than three live neighbours dies, as if by overcrowding.
if (num > 3) this.willBeAlive = false;
} else {
// 4. Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction.
if (num == 3) this.willBeAlive = true;
}
}
and here is a seed array for "Gosper's Glider Gun":
[[2,6],[2,7],[3,6],[3,7],[12,6],[12,7],[12,8],[13,5],[13,9],[14,4],[14,10],[15,4],[15,10],[16,7],[17,5],[17,9],[18,6],[18,7],[18,8],[19,7],[22,4],[22,5],[22,6],[23,4],[23,5],[23,6],[24,3],[24,7],[26,2],[26,3],[26,7],[26,8],[36,4],[36,5],[37,4],[37,5]]

Categories

Resources