def generate_slitherlink(height, width, symmetry=False, verbose=False): def no_neighboring_zero(problem): for y in range(height): for x in range(width): if problem[y][x] == 0: for dy in range(-1, 2): for dx in range(-1, 2): y2 = y + dy x2 = x + dx if (dy, dx) != ( 0, 0 ) and 0 <= y2 < height and 0 <= x2 < width and problem[ y2][x2] == 0: return False return True generated = generate_problem( lambda problem: solve_slitherlink(height, width, problem), builder_pattern=ArrayBuilder2D(height, width, range(-1, 4), default=-1, symmetry=symmetry, disallow_adjacent=True), clue_penalty=lambda problem: count_non_default_values( problem, default=-1, weight=5), pretest=no_neighboring_zero, verbose=verbose) return generated
def generate_simplegako(height, width, verbose=True, disallow_adjacent=False, symmetry=False, hard=False): pattern = range(max(height, width) + 1) if hard else range(height + width - 1) def pretest(problem): if not hard: return True for y in range(height): for x in range(width): if problem[y][x] > 0: for i in range(1, width - x): if problem[y][x+i] == problem[y][x]: return False for i in range(1, height - y): if problem[y+i][x] == problem[y][x]: return False mean = (height + width) // 2 count_big = 0 for y in range(height): for x in range(width): if problem[y][x] > 2: count_big += 1 return (count_big < mean) generated = generate_problem(lambda problem: solve_simplegako(height, width, problem), builder_pattern=ArrayBuilder2D(height, width, pattern, default=0, disallow_adjacent=disallow_adjacent, symmetry=symmetry), clue_penalty=lambda problem: count_non_default_values(problem, default=0, weight=5), pretest=pretest, verbose=verbose) return generated
def generate_yinyang(height, width, disallow_adjacent=False, no_clue_on_circumference=False, verbose=False): def pretest(problem): for y in range(height): if problem[y][0] != 0 or problem[y][-1] != 0: return False for x in range(width): if problem[0][x] != 0 or problem[-1][x] != 0: return False return True generated = generate_problem( lambda problem: solve_yinyang(height, width, problem), builder_pattern=ArrayBuilder2D(height, width, range(0, 3), default=0, disallow_adjacent=disallow_adjacent), clue_penalty=lambda problem: count_non_default_values( problem, default=0, weight=5), pretest=pretest if no_clue_on_circumference else None, verbose=verbose) return generated
def generate_creek(height, width, no_easy=False, verbose=False): pattern = [] for y in range(height + 1): row = [] for x in range(width + 1): nmax = (1 if y in (0, height) else 2) * (1 if x in (0, width) else 2) row.append( Choice([-1] + list( range(1 if no_easy else 0, nmax if no_easy else nmax + 1)), default=-1)) pattern.append(row) def pretest(problem): if not no_easy: return True for y in range(height + 1): for x in range(width + 1): if y < height and (problem[y][x], problem[y + 1][x]) in ((1, 3), (3, 1)): return False if x < width and (problem[y][x], problem[y][x + 1]) in ((1, 3), (3, 1)): return False return True generated = generate_problem( lambda problem: solve_creek(height, width, problem), builder_pattern=pattern, clue_penalty=lambda problem: count_non_default_values( problem, default=-1, weight=3), pretest=pretest, verbose=verbose) return generated
def generate_shakashaka(height, width, verbose=False): generated = generate_problem(lambda problem: solve_shakashaka(height, width, problem), builder_pattern=ArrayBuilder2D(height, width, [None, -1, 0, 1, 2, 3, 4], default=None, disallow_adjacent=True), clue_penalty=lambda problem: count_non_default_values(problem, default=None, weight=6), verbose=verbose) return generated
def generate_simpleloop(height, width, verbose): pivot = (random.randint(0, height - 1), random.randint(0, width - 1)) def pretest(problem): parity = [0, 0] for y in range(height): for x in range(width): if problem[y][x] == 1: continue a = (y + x) % 2 * 2 - 1 if (y, x) != pivot: parity[0] += a parity[1] += a return parity[0] == 0 or parity[1] == 0 generated = generate_problem( lambda problem: solve_simpleloop(height, width, problem, pivot), builder_pattern=ArrayBuilder2D(height, width, [0, 1], default=0, disallow_adjacent=True), clue_penalty=lambda problem: count_non_default_values( problem, default=0, weight=10), pretest=pretest, verbose=verbose) if generated is None: return None num_pass = 0 for y in range(height): for x in range(width): if (y, x) != pivot and generated[y][x] == 0: num_pass += 1 y, x = pivot generated[y][x] = 1 - num_pass % 2 return generated
def generate_yajilin(height, width, no_zero=False, no_max_clue=False, verbose=False): choices = [] for y in range(height): row = [] for x in range(width): c = ['..'] for i in range(1 if no_zero else 0, (y + 3) // 2 - (1 if no_max_clue else 0)): c.append('^{}'.format(i)) for i in range(1 if no_zero else 0, (x + 3) // 2 - (1 if no_max_clue else 0)): c.append('<{}'.format(i)) for i in range(1 if no_zero else 0, (height - y + 2) // 2 - (1 if no_max_clue else 0)): c.append('v{}'.format(i)) for i in range(1 if no_zero else 0, (width - x + 2) // 2 - (1 if no_max_clue else 0)): c.append('>{}'.format(i)) row.append(Choice(c, '..')) choices.append(row) generated = generate_problem( lambda problem: solve_yajilin(height, width, problem), builder_pattern=choices, clue_penalty=lambda problem: count_non_default_values( problem, default='..', weight=20), verbose=verbose) return generated
def generate_doppelblock(n, verbose=False): max_sum = (n - 2) * (n - 1) // 2 generated = generate_problem(lambda problem: solve_doppelblock(n, problem[0], problem[1]), builder_pattern=ArrayBuilder2D(2, n, [-1] + list(range(0, max_sum + 1)), default=-1), clue_penalty=lambda problem: count_non_default_values(problem, default=-1, weight=10), verbose=verbose) return generated
def generate_nurikabe(height, width, min_clue=None, max_clue=10, verbose=False): disallow_adjacent = [] for dy in range(-2, 3): for dx in range(-2, 3): if (dy, dx) != (0, 0): disallow_adjacent.append((dy, dx)) generated = generate_problem( lambda problem: solve_nurikabe( height, width, problem, unknown_low=min_clue), builder_pattern=ArrayBuilder2D(height, width, [-1, 0] + list(range(min_clue or 1, max_clue)), default=0, disallow_adjacent=True, symmetry=False), clue_penalty=lambda problem: count_non_default_values( problem, default=0, weight=5), verbose=verbose) if generated is None: return None else: return resolve_unknown(height, width, generated, unknown_low=min_clue)
def generate_fillomino(height, width, verbose=False): generated = generate_problem( lambda problem: solve_nurimisaki(height, width, problem), builder_pattern=ArrayBuilder2D(height, width, [-1, 0], default=-1), clue_penalty=lambda problem: count_non_default_values( problem, default=-1, weight=7), verbose=verbose) return generated
def generate_masyu(height, width, symmetry=False, verbose=False): generated = generate_problem( lambda problem: solve_masyu(height, width, problem), builder_pattern=ArrayBuilder2D(height, width, [0, 1, 2], default=0, symmetry=symmetry), clue_penalty=lambda problem: count_non_default_values( problem, default=0, weight=10), verbose=verbose) return generated
def generate_gokigen(height, width, no_easy=False, no_adjacent=False, verbose=False): pattern = [] for y in range(height + 1): row = [] for x in range(width + 1): lim = (1 if y in (0, height) else 2) * (1 if x in (0, width) else 2) row.append( Choice([-1] + list( range(1 if no_easy else 0, lim if no_easy else (lim + 1))), default=-1)) pattern.append(row) def pretest(problem): for y in range(height + 1): for x in range(width + 1): if no_adjacent: if y < height: if problem[y][x] != -1 and problem[y + 1][x] != -1: return False if x < width: if problem[y][x] != -1 and problem[y][x + 1] != -1: return False if no_easy: if y < height: if problem[y][x] in (1, 3) and problem[y + 1][x] in (1, 3): return False if x < width: if problem[y][x] in (1, 3) and problem[y][x + 1] in (1, 3): return False if y < height - 1: if problem[y][x] != -1 and problem[ y + 1][x] != -1 and problem[y + 2][x] != -1: return False if x < width - 1: if problem[y][x] != -1 and problem[y][ x + 1] != -1 and problem[y][x + 2] != -1: return False return True generated = generate_problem( lambda problem: solve_gokigen(height, width, problem), builder_pattern=pattern, clue_penalty=lambda problem: count_non_default_values( problem, default=-1, weight=2), pretest=pretest, verbose=verbose) return generated
def generate_firefly(height, width, min_clue=0, max_clue=5, verbose=False): cand = ['..'] for d in ['^', 'v', '<', '>']: cand.append(d + '?') for i in range(min_clue, max_clue + 1): cand.append(d + str(i)) generated = generate_problem( lambda problem: solve_firefly(height, width, problem), builder_pattern=ArrayBuilder2D(height, width, cand, default='..'), clue_penalty=lambda problem: count_non_default_values( problem, default='..', weight=10), verbose=verbose) return generated
def generate_building(size, verbose=False): initial, neighbor = build_neighbor_generator( [[Choice(range(0, size + 1), default=0) for _ in range(size)] for _ in range(4)]) generated = generate_problem( lambda problem: solve_building(size, *problem), initial, neighbor, clue_penalty=lambda problem: count_non_default_values( problem, default=0, weight=3.0), verbose=verbose) if generated is not None: return generated
def generate_fillomino(height, width, checkered=False, disallow_adjacent=False, symmetry=False, verbose=False): generated = generate_problem( lambda problem: solve_fillomino( height, width, problem, checkered=checkered), builder_pattern=ArrayBuilder2D(height, width, range(0, 9), default=0, disallow_adjacent=disallow_adjacent, symmetry=symmetry), clue_penalty=lambda problem: count_non_default_values( problem, default=0, weight=5), verbose=verbose) return generated
def generate_sudoku(n, max_clue=None, symmetry=False, verbose=False): size = n * n def pretest(problem): if max_clue is None: return True else: return count_non_default_values(problem, default=0, weight=1) <= max_clue generated = generate_problem( lambda problem: solve_sudoku(problem, n=n), builder_pattern=ArrayBuilder2D(size, size, range(0, size + 1), default=0, symmetry=symmetry), pretest=pretest, clue_penalty=lambda problem: count_non_default_values( problem, default=0, weight=5), verbose=verbose) return generated
def generate_koutano(height, width, hard=False, symmetry=False, verbose=False): pattern = list(range(1, height + width - 3)) if not hard: pattern += [0, height + width - 2] def pretest(problem): if not hard: return True for y in range(height): count_hint = 0 for x in range(width): count_hint += 1 if problem[y][x] > -1 else 0 if count_hint > 2: return False for x in range(width): count_hint = 0 for y in range(height): count_hint += 1 if problem[y][x] > -1 else 0 if count_hint > 2: return False return True generated = generate_problem( lambda problem: solve_koutano(height, width, problem), builder_pattern=ArrayBuilder2D(height, width, pattern, default=-1, symmetry=symmetry, disallow_adjacent=hard), clue_penalty=lambda problem: count_non_default_values( problem, default=-1, weight=2), pretest=pretest, verbose=verbose) return generated
def generate_akari(height, width, no_easy=False, verbose=False): def pretest(problem): visited = [[False for _ in range(width)] for _ in range(height)] def visit(y, x): if not (0 <= y < height and 0 <= x < width and problem[y][x] == -2 and not visited[y][x]): return visited[y][x] = True visit(y - 1, x) visit(y + 1, x) visit(y, x - 1) visit(y, x + 1) n_component = 0 for y in range(height): for x in range(width): if problem[y][x] == -2 and not visited[y][x]: n_component += 1 visit(y, x) if n_component != 1: return False if not no_easy: return True for y in range(height): for x in range(width): if problem[y][x] >= 0: n_adj = (1 if y > 0 and problem[y - 1][x] == -2 else 0) + (1 if x > 0 and problem[y][x - 1] == -2 else 0) + (1 if y < height - 1 and problem[y + 1][x] == -2 else 0) + (1 if x < width - 1 and problem[y][x + 1] == -2 else 0) if problem[y][x] >= n_adj - 1: return False return True pattern = [-2, -1, 1, 2] if no_easy else [-2, -1, 0, 1, 2, 3, 4] generated = generate_problem(lambda problem: solve_akari(height, width, problem), builder_pattern=ArrayBuilder2D(height, width, pattern, default=-2, symmetry=True), clue_penalty=lambda problem: count_non_default_values(problem, default=-2, weight=5), pretest=pretest, verbose=verbose) return generated
def generate_amarune(height, width, verbose=True): pattern = [] cand = Choice([-1] + list(range(1, height * width - 1)), -1) pattern += [[cand for _ in range(width)] for _ in range(height)] cand = Choice([-1, 0, 1, 2, 3], -1) pattern += [[cand for _ in range(width - 1)] for _ in range(height)] pattern += [[cand for _ in range(width)] for _ in range(height - 1)] def pretest(problem): count_number_hint = 0 for y in range(height): for x in range(width): if problem[y][x] > 0: count_number_hint += 1 return (count_number_hint < 3) generated = generate_problem( lambda problem: solve_amarune(height, width, problem), builder_pattern=pattern, clue_penalty=lambda problem: count_non_default_values( problem, default=-1, weight=2), pretest=pretest, verbose=verbose) return generated
def pretest(problem): if max_clue is None: return True else: return count_non_default_values(problem, default=0, weight=1) <= max_clue