def solve(cls, game): "Solve the board" oldhash = hash(str(game.rows)) # Step 1: individual lines columns = game.cols yinfo = game.yinfo for i in range(game.width): debug(Rule(f"Solve column #{i}")) game.replacecol( i, NonogramLineSolver.solve(columns[i], yinfo[i])) rows = game.rows xinfo = game.xinfo for i in range(game.height): debug(Rule(f"Solve row #{i}")) game.replacerow( i, NonogramLineSolver.solve(rows[i], xinfo[i])) if cls.issolved(game): debug(game, Rule(title="Solving completed")) return True newhash = hash(str(game.rows)) if oldhash != newhash: debug(game, Rule(title="Next solving cycle")) return cls.solve(game) debug(game, Rule(title="Solving failed")) return False
def solve_singlerangemultireq(cls, values, requirements): "Solving method: More than 1 requirement in 1 range" debug("[red][Solving singlerange] Start[/]") debug({"values": values, "requirements": requirements}) debug("[red][Solving singlerange] End[/]") return values
def solve_multirange(cls, values, requirements): "Solving method: Multirange" ranges = cls.getfreeranges(values) debug("[red][Solving multirange] Start[/]") debug({"values": values, "ranges": ranges, "requirements": requirements}) debug("[red][Solving multirange] End[/]") return values
def solve_fullline(cls, values, requirements): "Solving method: Requirements use full space" reqwidth = cls.getfullwidth(requirements) valwidth = len(values) debug("[yellow][Solving fullline] Start[/]") debug({"reqwidth": reqwidth, "valwidth": valwidth}) if reqwidth > valwidth: raise UnsolvableLine("Requirements use more space than possible!") if valwidth == reqwidth: index = 0 for req in requirements: values[index:index+req] = [True]*req index += req+1 debug("[yellow][Solving fullline] Succeeded[/]") return values debug("[yellow][Solving fullline] Failed[/]") return values
def solve(cls, values, requirements): "Try to solve a line" oldhash = hash(str(values)) debug({"values": values, "requirements": requirements}) # START) Skip if line is already full if cls.iscompleted(values, requirements): if not cls.isfull(values): values = cls.fillline(values) debug(values) return values # 1a) Trim from ends offset = cls.getoffset(values) trim_start = 0 i_first = offset[1] if values[i_first] is True: i_end = i_first+requirements[0] values[i_first:i_end] = [True]*requirements[0] if not i_end < len(values): cls.fillline(values) debug("[cyan][Return] Solved in 1a) start:[/]", values) return values values[i_end] = False trim_start = i_end+1 offset = cls.getoffset(values) trim_end = 0 i_last = -1-offset[2] if values[i_last] is True: i_start = i_last-requirements[-1]+1 values[i_start:i_last+1 or None] = [True]*requirements[-1] if not (len(values) + i_start) > 0: values = cls.fillline(values) debug("[cyan][Return] Solved in 1a) end:[/]", values) return values values[i_start-1] = False trim_end = i_start-1 # 1b) End if requirements are met if cls.iscompleted(values, requirements): values = cls.fillline(values) debug("[cyan][Return] Solved in 1a):[/]", values) return values # 1c) Run for sublist if ends can be trimmed if trim_start or trim_end or offset[0]: debug("[blue][Recursive] Trimming line[/]", {"t_start": trim_start, "t_end": trim_end, "offset": offset}) if trim_start and trim_end: values[trim_start:trim_end] = cls.solve( values[trim_start:trim_end], requirements[1:-1]) elif trim_start: values[trim_start:-offset[2] or None] = cls.solve( values[trim_start:-offset[2] or None], requirements[1:] ) elif trim_end: values[offset[1]:trim_end] = cls.solve( values[offset[1]:trim_end], requirements[:-1] ) else: values[offset[1]:-offset[2] or None] = cls.solve( values[offset[1]:-offset[2] or None], requirements ) debug(values) return values # 2a) Solve fullline values = cls.solve_fullline(values, requirements) # 2b) End if requirements are met if cls.iscompleted(values, requirements): values = cls.fillline(values) debug("[cyan][Return] Solved in 2) Fullline:[/]", values) return values # 3a) Solve ranges values = cls.solve_ranges(values, requirements) # 3b) End if requirements are met if cls.iscompleted(values, requirements): values = cls.fillline(values) debug("[cyan][Return] Solved in 3) Ranges:[/]", values) return values # END) Solve again if something has changed newhash = hash(str(values)) if oldhash != newhash: debug("[blue][Recursive] Line changed - solve again[/]") return cls.solve(values, requirements) debug("[cyan][Return] Line still unsolved:[/]", values) return values
def solve_ranges(cls, values, requirements): "Solving method: Use free ranges" ranges = cls.getfreeranges(values) debug("[yellow][Solving ranges] Start[/]") debug({"ranges": ranges}) # Remove all ranges which aren't usable smallestreq = min(requirements) fillempty = len(ranges) > 1 and countin( map(lambda x: True in x[2], ranges), True) == len(requirements) for ran in ranges: if ran[0] < smallestreq or (fillempty and not True in ran[2]): values[ran[1][0]:ran[1][1]+1] = [False]*ran[0] # Update ranges var ranges = cls.getfreeranges(values) # Modify ranges at the end and start for ran in ranges: if ran[0] < requirements[0]: values[ran[1][0]:ran[1][1]+1] = [False]*ran[0] continue if ran[2][0] is True or (ran[0] == requirements[0] and True in ran[2]): values[ran[1][0]:ran[1][0]+requirements[0] ] = [True]*requirements[0] if ran[1][0]+requirements[0] < len(values): values[ran[1][0]+requirements[0]] = False break for ran in ranges[::-1]: if ran[0] < requirements[-1]: values[ran[1][0]:ran[1][1]+1] = [False]*ran[0] continue if ran[2][-1] is True or (ran[0] == requirements[-1] and True in ran[2]): values[ran[1][1]-requirements[-1]+1:ran[1] [1]+1] = [True]*requirements[-1] if ran[1][1]-requirements[-1] >= 0: values[ran[1][1]-requirements[-1]] = False break # Update ranges var ranges = cls.getfreeranges(values) # Case "Exact match" if list(map(lambda x: x[0], ranges)) == requirements: for ran in ranges: values[ran[1][0]:ran[1][1]+1] = [True]*ran[0] debug("[yellow][Solving ranges] Succeeded with exact match[/]") return values elif countin(map(lambda x: True in x[2], ranges), True) == len(requirements): for i, ran in enumerate(ranges): req = requirements[i] values[ran[1][0]:ran[1][1]+1] = cls.solve_singlerangesinglereq(ran[2], req) return values # Case "Only one requirement in one range" if len(requirements) == 1 and len(ranges) == 1: req = requirements[0] ran = ranges[0] values[ran[1][0]:ran[1][1] + 1] = cls.solve_singlerangesinglereq(ran[2], req) # Case "More than one requirement in one range" elif len(ranges) == 1: ran = ranges[0] values[ran[1][0]:ran[1][1] + 1] = cls.solve_singlerangemultireq(ran[2], requirements) # Case "More than one requirement in more than one range" elif len(ranges) > 1 and len(requirements) > 1: s = slice(ranges[0][1][0], ranges[-1][1][1]+1) values[s] = cls.solve_multirange(values[s], requirements) debug("[yellow][Solving ranges] Ended[/]") return values