Initial
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
import sys
|
||||
from typing import Iterable, Iterator, TYPE_CHECKING
|
||||
|
||||
|
||||
class State(int):
|
||||
def get_digit(self, idx): # 0 开始
|
||||
return self // (10 ** idx) % 10
|
||||
|
||||
def with_digit(self, idx: int, val: int) -> "State":
|
||||
return State(self + (val - self.get_digit(idx)) * 10 ** idx)
|
||||
|
||||
def get_coord_digit(self, row: int, col: int):
|
||||
idx = 3 * (3 - row) - (col + 1)
|
||||
return self.get_digit(idx)
|
||||
|
||||
def with_coord_digit(self, row: int, col: int, val: int) -> "State":
|
||||
idx = 3 * (3 - row) - (col + 1)
|
||||
return self.with_digit(idx, val)
|
||||
|
||||
def get_number_coord(self, n: int = 0) -> tuple[int, int]:
|
||||
for r in range(3):
|
||||
for c in range(3):
|
||||
if self.get_coord_digit(r, c) == n:
|
||||
return r, c
|
||||
raise ValueError(f"No {n} in state {self}")
|
||||
|
||||
def get_possible_steps(self) -> Iterable["State"]:
|
||||
x0, y0 = self.get_number_coord()
|
||||
if x0 > 0:
|
||||
yield self.with_coord_digit(x0, y0, self.get_coord_digit(x0 - 1, y0)) \
|
||||
.with_coord_digit(x0 - 1, y0, 0)
|
||||
if y0 > 0:
|
||||
yield self.with_coord_digit(x0, y0, self.get_coord_digit(x0, y0 - 1)) \
|
||||
.with_coord_digit(x0, y0 - 1, 0)
|
||||
if x0 < 2:
|
||||
yield self.with_coord_digit(x0, y0, self.get_coord_digit(x0 + 1, y0)) \
|
||||
.with_coord_digit(x0 + 1, y0, 0)
|
||||
if y0 < 2:
|
||||
yield self.with_coord_digit(x0, y0, self.get_coord_digit(x0, y0 + 1)) \
|
||||
.with_coord_digit(x0, y0 + 1, 0)
|
||||
|
||||
def compare_with(self, other_state: "State") -> int:
|
||||
# return 0
|
||||
# return 9 - sum(self.get_digit(idx) == other_state.get_digit(idx) for idx in range(9))
|
||||
# return max(0, 8 - sum(self.get_digit(idx) == other_state.get_digit(idx) for idx in range(9)))
|
||||
d1 = sum(self.get_digit(idx) != other_state.get_digit(idx) for idx in range(9))
|
||||
if self.get_number_coord() != other_state.get_number_coord():
|
||||
d1 -= 1
|
||||
d2 = self.manhattan_distance(other_state)
|
||||
if d1 > d2: print("?????????", self, other_state, d1, d2)
|
||||
return d1
|
||||
|
||||
def manhattan_distance(self, other_state: "State") -> int:
|
||||
number_coords = {}
|
||||
other_number_coords = {}
|
||||
for r in range(3):
|
||||
for c in range(3):
|
||||
number_coords[self.get_coord_digit(r, c)] = (r, c)
|
||||
other_number_coords[other_state.get_coord_digit(r, c)] = (r, c)
|
||||
|
||||
# del number_coords[0]
|
||||
# del other_number_coords[0]
|
||||
return sum(
|
||||
abs(number_coords[i][0] - other_number_coords[i][0]) + abs(number_coords[i][1] - other_number_coords[i][1])
|
||||
for i in range(1, 9))
|
||||
|
||||
|
||||
class StateNode:
|
||||
def __init__(self,
|
||||
state: State | int | str, cost: int,
|
||||
heuristic_cost: int,
|
||||
previous_state: State | None = None):
|
||||
self.state = State(state)
|
||||
self.cost = cost # g
|
||||
self.heuristic_cost = heuristic_cost # h
|
||||
self.previous_state = previous_state
|
||||
self.running_iterator: Iterator[State] | None = None
|
||||
|
||||
@property
|
||||
def evaluated_cost(self):
|
||||
return self.heuristic_cost + self.cost
|
||||
|
||||
def __lt__(self, other):
|
||||
return self.evaluated_cost < other.evaluated_cost
|
||||
|
||||
def __repr__(self):
|
||||
return f"{self.state} (g={self.cost}, h={self.heuristic_cost})"
|
||||
|
||||
|
||||
def dfs_solve(original_state: State, final_state: State, max_cost: int = 2) -> int | bool:
|
||||
open_list: list[StateNode] = [StateNode(original_state, 0, original_state.compare_with(final_state))]
|
||||
next_max_cost: int | float = float("inf")
|
||||
while open_list:
|
||||
current_node = open_list[-1]
|
||||
print(open_list, file=sys.stderr)
|
||||
if current_node.state == final_state:
|
||||
return True
|
||||
|
||||
if current_node.running_iterator is None:
|
||||
current_node.running_iterator = iter(current_node.state.get_possible_steps())
|
||||
continue
|
||||
|
||||
try:
|
||||
new_state = next(current_node.running_iterator)
|
||||
if new_state == current_node.previous_state:
|
||||
continue
|
||||
new_node = StateNode(new_state, current_node.cost + 1, new_state.compare_with(final_state),
|
||||
current_node.state)
|
||||
if new_node.evaluated_cost > max_cost:
|
||||
next_max_cost = min(new_node.evaluated_cost, next_max_cost)
|
||||
continue
|
||||
open_list.append(new_node)
|
||||
except StopIteration:
|
||||
open_list.pop()
|
||||
if TYPE_CHECKING:
|
||||
return int(next_max_cost)
|
||||
else:
|
||||
return next_max_cost
|
||||
|
||||
|
||||
def ida_star_solve(original_state: State, final_state: State) -> int:
|
||||
max_cost = original_state.compare_with(final_state)
|
||||
while True:
|
||||
print(max_cost, "!!!!!!", file=sys.stderr)
|
||||
result = dfs_solve(original_state, final_state, max_cost)
|
||||
if result is True:
|
||||
return max_cost
|
||||
max_cost = result
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
original_state = State(input())
|
||||
final_state = State(123804765)
|
||||
print(ida_star_solve(original_state, final_state))
|
||||
Reference in New Issue
Block a user