# Minimum moves to lose grid game

Here is a number grid: And here is the rule for how to move:

A move consists of selecting two adjacent squares (either vertically, horizontally or diagonally) and dividing the sum of the two numbers evenly among those two squares. In the case that the sum of the numbers is odd, add an extra one in the square which originally had the greatest number.

Your challenge is to find the minimum number of moves it takes for all the squares in the grid to be greyed out without all the numbers ending with 22. (Note: Now I'm going to use the word lose to describe freezing the whole grid without each square in the grid ending with 22.)

Here is an example of me losing on the spot, the first one at normal speed, the second one a time lapse. I ended up with 45, though that was a random no-strategy play:

I programmed the app/tool/game I used in the example above using python. You can use the code to also have access to the program:

import pygame

# You can change the grid & size to whatever you like
grid = [[33, 7, 35, 26, 30],
[1, 8, 38, 29, 17],
[16, 19, 9, 37, 21],
[20, 36, 3, 11, 15],
[32, 14, 34, 31, 28]]

size = 80
line = 5
win = sum([c for r in grid for c in r]) // len([c for r in grid for c in r])

pygame.init()
pygame.font.init()

font = pygame.font.SysFont("Arial", size-10)
wn = pygame.display.set_mode((600, 600))

grid = [c for c in zip(*grid)]
class Square():
def __init__(self, pos, num):
self.x = pos * size + line
self.y = pos * size + line
self.num = num
self.color = (255, 255, 255)
self.rect = pygame.Rect(self.x, self.y, size-line, size-line)
self.frozen = False

def clear(self):
self.color = (255, 255, 255)

def draw(self):
if self.frozen:
pygame.draw.rect(wn, (150, 150, 150), self.rect)
else:
pygame.draw.rect(wn, self.color, self.rect)
if self.num == win:
pygame.draw.circle(wn, (100, 255, 100), (self.x+size//2, self.y+size//2), size//4)
text = font.render(str(self.num), True, (0, 0, 0))
if len(str(self.num)) == 1:
wn.blit(text, (self.x+size*.25, self.y*.98))
else:
wn.blit(text, (self.x+size*.055, self.y*.98))

class Box():

def __init__(self, grid, square=None):
self.square = square
self.grid = grid
self.clicked = []

def box(self): # Returns a list of all adjancent squares that can change the number of the selected square
x, y = self.square.x//size, self.square.y//size
y1 = x-1 if x else 0
y2 = len(self.grid)+2 if x > len(self.grid)+2 else x+2
x1 = y-1 if y else 0
x2 = len(self.grid)+2 if y > len(self.grid)+2 else y+2
b = []
for r in self.grid[y1:y2]:
for c in r[x1:x2]:
if abs(c.num - self.grid[x][y].num) > 1:
b.append(c)
elif c != self.square:
c.clear()
return b

def color(self, color):
for square in self.box():
square.color = color

def clear(self):
for c in self.clicked:
c.clear()
self.clicked.clear()

def show_moves(num):
moves = f'{num} move' if num == 1 else f'{num} moves'
text = font.render(moves, True, (255, 255, 255))
wn.blit(text, (line, size*len(squares)))

def avg(n1, n2):
n = n1 + n2
if n % 2:
if n1 > n2:
return n // 2 + 1, n // 2
return n // 2, n // 2 + 1
return n // 2, n // 2

def won(grid):
if all(square.num == win for row in grid for square in row):
return 'You Win!'
return 'You Lose!'

squares = [[Square((i, j), col) for j, col in enumerate(row)] for i, row in enumerate(grid)]
box = Box(squares)
box2 = Box(squares)
total = 0

while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
if event.type == pygame.MOUSEBUTTONDOWN:
for row in squares:
for square in row:
if square.rect.collidepoint(pygame.mouse.get_pos()):
if not square.frozen:
if not box.clicked:
box.clicked.append(square)
square.color = (140, 255, 255)
box.square = square
box.color((255, 255, 140))
else:
if square in box.box():
box.clicked.append(square)
if square == box.clicked:
box.color((255, 255, 255))
box.clear()
if len(box.clicked) == 2:
total += 1
box.clicked.num, box.clicked.num = avg(box.clicked.num, box.clicked.num)
box.color((255, 255, 255))
box.clear()
wn.fill((0, 0, 0))
for i in range(len(squares)):
for j in range(len(squares[i])):
for k in range(max(0,i-1), min(len(squares), i+2)):
for l in range(max(0,j-1), min(len(squares[k]), j+2)):
if i != k or j != l:
if not any((k, l) for (k, l) in adjacent if abs(squares[i][j].num - squares[k][l].num) > 1):
squares[i][j].frozen = True
elif squares[i][j].frozen:
squares[i][j].frozen = False
for row in squares:
for square in row:
square.draw()
show_moves(total)
pygame.display.update()
if all(square.frozen for row in squares for square in row):
wn.fill((0, 0, 0))
text = font.render(won(squares), True, (255, 255, 255))
wn.blit(text, (line, size*len(squares)//2))
show_moves(total)
pygame.display.update()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
break

• What causes a square to be greyed out? Nov 1, 2020 at 18:06
• @Anthony It appears that the grey squares are those with no move options. (All neighboring squares are either equal or +/- 1 in value.) A grey square can become white again if one of its neighbors' values changes. Nov 1, 2020 at 18:19
• @AnthonyIngram-Westover What Daniel said :) Nov 1, 2020 at 18:25
• Thank you for the split-speed animations!
– humn
Nov 1, 2020 at 18:48
• @bobble I can't believe I didn't notice that! Nov 1, 2020 at 19:09