# Minimum number of moves to win this number game

This puzzle is inspired by this puzzle that I answered: Is equality possible?

Here is the number grid from that puzzle:

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 have the number 13 in it. (Note: Now I'm going to use the word win to describe completing a grid)

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

As you can probably tell, it is quite simple. There are countless ways to win. A coordinated strategy isn't necessary if all you need is to win, it's just a matter of eyeballing which two squares can nudge you closer to winning. The case might be different if you want to minimize the number of moves.

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 = [[7, 24, 12, 8, 11],
[13, 21, 3, 20, 19],
[10, 22, 15, 2, 9],
[23, 1, 6, 16, 17],
[5, 25, 14, 4, 18]]

size = 60
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[0] * size + line
self.y = pos[1] * 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[0])+2 if y > len(self.grid[0])+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[0]:
box.color((255, 255, 255))
box.clear()
if len(box.clicked) == 2:
total += 1
box.clicked[0].num, box.clicked[1].num = avg(box.clicked[0].num, box.clicked[1].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