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