Go Zendo

Conjecture: The rule is invariant to rotating and mirroring the board.

2 Likes

I confirm :slight_smile:

image image

Guess: All chains must have the same number of liberties.


Edit: Nevermind.

image

This destroys that idea, but I do feel like itā€™s something about the number of liberties.

1 Like

Yes, I see you found a counter-example. Itā€™s a good guess though :slight_smile:

Conjecture: A board with only one color will always be green.

1 Like

True again! You are making great progress :smile:

Conjecture: A completely filled board (no empty intersections) will always be green.

2 Likes

Yes sir!

Rule guess: If two chains are connected, they must have the same number of liberties.

3 Likes

Yes thatā€™s the rule! Nice work both of you. @RubyMineshaft was already very close with their guess, and @le_4TC refined it.

Apparently this rule was a piece of cake for the two of you, Iā€™ll have to think of a more difficult rule next time :rofl:

Implementing the rule was a nice exercise :wink:

3 Likes

Yeah, Ruby did most of the work :smiley:

1 Like

Would you mind sharing your code? Iā€™d be very interested in seeing how you implemented it.


Iā€™m also curious if the way I wrote mine is the best I could have done.

const checkMatch = () => {
  for (x = 0; x < board.size; x++) { //for each col
    for (y = 0; y < board.size; y++) { // for each position in col
      if (board[x][y].color === 1) {  // if node is black
        let col = 0;
        let row = 0;
        for (let i = 0; i < board.size; i++) { // count number of stones in row and col
          if (board[x][i].color !== 0) col++;
          if (board[i][y].color !== 0) row++;
        }
        if (col === 1 && row === 1) {  // return true if black stone is the only stone in row and col
          return true;
        }
      }
    }
  }
  return false;
}

Does anything immediately strike you guys as inefficient?

2 Likes

Efficiency is probably less important than other factors in this case (ease of implementation and risk of making a mistake for instance), but of course itā€™s fun to think about optimizing anyways :smiley:

The current implementation looks to be O(n^3), we could get it down to O(n^2) like this:

Start in the upper left. Step to the right until you find a black stone (if you encounter a white stone or no stones, skip to next row). If youā€™ve found a black stone, first make sure that the rest of the row is empty (if itā€™s not, skip to next row). Then go back to the found black stone, and check that the rest of its collumn is empty. If it is, weā€™re done, otherwise go to the next row and keep looking.

3 Likes

I need to admit that I didnā€™t try to create the most efficient algorithm. The basic concept is a breadth-first search that is used to identify chains, count their liberties and also find which chains are adjacent to each other.

I fear that it may not be easily readable though. I can write some comments if you want, just tell me. Anyway hereā€™s the code:

Code

check() {
var mark = []
var queue = []
var queue_chains = []
var color = 0
var x1 = 0, y1 = 0, x2 = 0, y2 = 0, x3 = 0, y3 = 0, x4 = 0, y4 = 0
var libertycount = 0, liberty_check = 0
var liberties_adjacent = []
var i = 0
var problem = false

for (x1 = 0; x1 < this.width; x1++) {
  mark[x1] = []
  for (y1 = 0; y1 < this.height; y1++) {
    mark[x1][y1] = 0
  }
}

for (x1 = 0; x1 < this.width; x1++) {
  for (y1 = 0; y1 < this.height; y1++) {

    if ((mark[x1][y1] == 0) && (this[x1][y1].color != 0)) {
      queue_chains.push(x1);
      queue_chains.push(y1);
      while (queue_chains.length > 1) {
        x4 = queue_chains.shift()
        y4 = queue_chains.shift()
        if ((mark[x4][y4] == 0) && (this[x4][y4].color != 0)) {
          queue.push(x4);
          queue.push(y4);
          mark[x4][y4] = 1;
          color = this[x4][y4].color;
          
          while (queue.length > 0) {
            x2 = queue.shift();
            y2 = queue.shift();

            if (x2 > 0) {
              x3 = x2 - 1;
              y3 = y2;

              if (mark[x3][y3] == 0) {
                if (this[x3][y3].color == color) {
                  queue.push(x3);
                  queue.push(y3);
                  mark[x3][y3] = 1;
                }
                if (this[x3][y3].color == - color) {
                  queue_chains.push(x3);
                  queue_chains.push(y3);
                }
                if (this[x3][y3].color == 0){
                  libertycount ++;
                  mark[x3][y3] = 2;
                }
              }
            }

            if (x2 < this.width - 1) {
              x3 = x2 + 1;
              y3 = y2;

              if (mark[x3][y3] == 0) {
                if (this[x3][y3].color == color) {
                  queue.push(x3);
                  queue.push(y3);
                  mark[x3][y3] = 1;
                }
                if (this[x3][y3].color == - color) {
                  queue_chains.push(x3);
                  queue_chains.push(y3);
                }
                if (this[x3][y3].color == 0){
                  libertycount ++;
                  mark[x3][y3] = 2;
                }
              }
            }

            if (y2 > 0) {
              x3 = x2;
              y3 = y2 - 1;

              if (mark[x3][y3] == 0) {
                if (this[x3][y3].color == color) {
                  queue.push(x3);
                  queue.push(y3);
                  mark[x3][y3] = 1;
                }
                if (this[x3][y3].color == - color) {
                  queue_chains.push(x3);
                  queue_chains.push(y3);
                }
                if (this[x3][y3].color == 0){
                  libertycount ++;
                  mark[x3][y3] = 2;
                }
              }
            }

            if (y2 < this.height - 1) {
              x3 = x2;
              y3 = y2 + 1;

              if (mark[x3][y3] == 0) {
                if (this[x3][y3].color == color) {
                  queue.push(x3);
                  queue.push(y3);
                  mark[x3][y3] = 1;
                }
                if (this[x3][y3].color == - color) {
                  queue_chains.push(x3);
                  queue_chains.push(y3);
                }
                if (this[x3][y3].color == 0){
                  libertycount ++;
                  mark[x3][y3] = 2;
                }
              }
            }
          }
          
          liberties_adjacent.push(libertycount)
          libertycount = 0;
          for (x2 = 0; x2 < this.width; x2++) {
            for (y2 = 0; y2 < this.height; y2++) {
              if (mark[x2][y2] == 2) {
                mark[x2][y2] = 0;
              }
            }
          }
        }
      }
      
      liberty_check = liberties_adjacent[0]
      for (i in liberties_adjacent) {
        if (liberty_check != liberties_adjacent[i]) {
          problem = true;
        }
      }
      liberties_adjacent = [];
    }
  }
}

return !problem
}

3 Likes

Thanks for sharing! I only had time to give it a quick look right now, but I look forward to reading it more thoroughly later today :slight_smile:

2 Likes

Iā€™ve implemented a new rule at zendo.4tc.xyz. Itā€™s probably a little bit harder than the last one, but hopefully not too bad :slightly_smiling_face:

The rule would work on any graph (so itā€™s invariant to rotating/mirroring, and it doesnā€™t mention the gridlines of the board).

As usual, Iā€™ll confirm or provide counterexamples to any rule guesses or conjectures.

3 Likes

Here are two boards to get you started:
imageimage

3 Likes

image
image
image

2 Likes

It seems that a lot of patterns with more than one stone are turning up red. Here are the only green multi-stone examples that Iā€™ve found so far:

Are all boards with only one stone green?

2 Likes

Correct.

2 Likes