リスト : 左優先則 (test.py) class A: def method(self): super().method() print("A") class B: def method(self): print("B") class C(A, B): def method(self): super().method() print("C") print(C.mro()) c = C() c.method()
C>python test.py [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>] B A C
A B C │ │ │ │ │ │ D E F \ │ / \│/ G G→D→A→E→B→F→C 図 : 深さ優先則
リスト : 深さ優先則 (test.py) class A: def method(self): super().method() print("A") class B: def method(self): print("B") class C(A): def method(self): super().method() print("C") class D(B): def method(self): super().method() print("D") class E(C, D): def method(self): super().method() print("E") print(E.mro()) e = E() e.method()
C>python test.py [<class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class '__main__.D'>, <class '__main__.B'>, <class 'object'>] B D A C E
A / \ / \ B C D→B→C→A \ / \ / D 図 : 合流則
リスト : 合流則 (test.py) class A: def method(self): print("A") class B(A): def method(self): super().method() print("B") class C(A): def method(self): super().method() print("C") class D(B, C): def method(self): super().method() print("D") print(D.mro()) d = D() d.method()
C>python test.py [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>] A C B D
A / B Mixin A / \ Mixin B \ / \ / C D 図 : Mix-in
名前 | 機能 |
---|---|
obj.member(func) | func が真となる要素を返す |
obj.position(func) | func が真となる要素の位置を返す |
obj.count(func) | func が真となる要素の個数を返す |
obj.map(func) | 要素に func を適用した結果を返すジェネレータを生成する |
obj.filter(func) | func が真となる要素を返すジェネレータを生成する |
obj.fold(func, init) | すべての要素を func を用いて結合した結果を返す |
リスト : Mix-in 用のクラス Enumerable class Enumerable: # 探索 def member(self, func): for x in self.each(): if func(x): return x return False # 位置を返す def position(self, func): n = 0 for x in self.each(): if func(x): return n n += 1 return -1 # 個数を数える def count(self, func): n = 0 for x in self.each(): if func(x): n += 1 return n # マップ def map(self, func): for x in self.each(): yield(func(x)) # フィルター def filter(self, func): for x in self.each(): if func(x): yield(x) # 畳み込み def fold(self, func, init): a = init for x in self.each(): a = func(a, x) return a
class LinkedList(Enumerable): ... class FixedList(LinkedList, Enumerable): ... class MyList(FixedList, Enumeraable) ...
>>> from linkedlist import * >>> from enumerable import * >>> class MyList(FixedList, Enumerable): ... def __init__(self, *args): ... super().__init__(*args) ... >>> a = MyList(8, 1, 2, 3, 4, 5, 6, 7, 8) >>> a FixedList(8, 1, 2, 3, 4, 5, 6, 7, 8) >>> a.member(lambda x: x % 2 == 0) 2 >>> a.position(lambda x: x % 2 == 0) 1 >>> a.count(lambda x: x % 2 == 0) 4 >>> list(a.map(lambda x: x * x)) [1, 4, 9, 16, 25, 36, 49, 64] >>> list(a.filter(lambda x: x % 2 == 0)) [2, 4, 6, 8] >>> a.fold(lambda x, y: x + y, 0) 36
リスト : 例外処理 try: 処理A 処理B 処理C except except_type: 処理D 処理E else: 処理F
>>> def foo(a, b): ... try: ... return a / b ... except ZeroDivisionError: ... print('Error {} / {}'.format(a, b)) ... return 0 ... >>> foo(10, 2) 5.0 >>> foo(1, 0) Error 1 / 0 0 >>> foo(1, '0') Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 3, in foo TypeError: unsupported operand type(s) for /: 'int' and 'str'
raise exception
>>> raise Exception Traceback (most recent call last): File "<stdin>", line 1, in <module> Exception >>> raise Exception('Oops') Traceback (most recent call last): File "<stdin>", line 1, in <module> Exception: Oops >>> try: ... raise Exception('oops', 123) ... except Exception as err: ... print(err.args) ... print(err) ... ('oops', 123) ('oops', 123)
>>> class FooError(Exception): pass ... >>> raise FooError('oops!') Traceback (most recent call last): File "<stdin>", line 1, in <module> __main__.FooError: oops!
リスト : finally 節 try: 処理A 処理B 処理C except except_type: 処理D 処理E else: 処理F finally: 処理G
>>> try: ... print('oops') ... finally: ... print('clean up!!') ... oops clean up!! >>> try: ... raise Exception('oops') ... finally: ... print("clean up!!") ... clean up!! Traceback (most recent call last): File "<stdin>", line 2, in <module> Exception: oops
with expr as var, expr1 as var1, ...: 処理 ...
def __enter__(self): ... def __exit__(self, exc_type, exc_value, traceback): ...
リスト : with 文の簡単な使用例 class Foo: def __init__(self, a): self.a = a print("init") def __enter__(self): print("enter") return self def __exit__(self, exc_type, exc_value, traceback): print("exit") print(exc_type) print(exc_value) print(traceback) return True with Foo(123) as a: print(a.a) with Foo(0) as a: raise Exception('oops!', a.a)
C> python3 test.py init enter 123 exit None None None init enter exit <class 'Exception'> ('oops!', 0) <traceback object at 0x0000023D79F61E88>
単純な二分木です。詳しい説明は拙作のページ「Algorithms with Python: 二分木とヒープ」をお読みくださいませ。
リスト : 二分木 (tree.py) # 節 class Node: def __init__(self, item, left = None, right = None): self.item = item self.left = left self.right = right # # 操作関数 # # 探索 def search(node, x): while node is not None: if node.item == x: return True elif x < node.item: node = node.left else: node = node.right return False def search_min(node): while node.left is not None: node = node.left return node.item def search_max(node): while node.right is not None: node = node.right return node.item # 挿入 def insert(node, x): if node is None: return Node(x) elif x < node.item: node.left = insert(node.left, x) elif x > node.item: node.right = insert(node.right, x) return node # 削除 def delete_min(node): if node.left is None: return node.right node.left = delete_min(node.left) return node def delete_max(node): if node.right is None: return node.left node.right = delete_max(node.right) return node def delete(node, x): if node is None: return node if node.item == x: if node.left is None: return node.right if node.right is None: return node.left node.item = search_min(node.right) node.right = delete_min(node.right) elif x < node.item: node.left = delete(node.left, x) else: node.right = delete(node.right, x) return node # 巡回 def each(node): if node is not None: yield from each(node.left) yield node.item yield from each(node.right) # 二分木 class Tree: def __init__(self): self.root = None # 挿入 def insert(self, x): self.root = insert(self.root, x) # 探索 def search(self, x): return search(self.root, x) def max(self): if self.root is None: return None return search_max(self.root) def min(self): if self.root is None: return None return search_min(self.root) # 削除 def delete(self, x): self.root = delete(self.root, x) def delete_max(self): if self.root is not None: delete_max(self.root) def delete_min(self): if self.root is not None: delete_min(self.root) # 巡回 def each(self): return each(self.root) # 空か? def is_empty(self): return self.root is None # 表示 def __repr__(self): if self.root is None: return 'Tree()' s = 'Tree(' for x in each(self.root): s += '{}, '.format(x) s = s[:-2] s += ')' return s # テスト if __name__ == '__main__': import random tree = Tree() data = [random.randint(0, 100) for x in range(10)] print(data) print(tree) print(tree.is_empty()) for x in data: tree.insert(x) print(tree) print(tree.is_empty()) print('max =', tree.max()) print('min =', tree.min()) print("delete max") tree.delete_max() print(tree) print("delete min") tree.delete_min() print(tree) for x in data: print('search', x, tree.search(x)) print('delete', x) tree.delete(x) print('search', x, tree.search(x)) print(tree)
[82, 37, 15, 7, 93, 75, 21, 71, 47, 25] Tree() True Tree(7, 15, 21, 25, 37, 47, 71, 75, 82, 93) False max = 93 min = 7 delete max Tree(7, 15, 21, 25, 37, 47, 71, 75, 82) delete min Tree(15, 21, 25, 37, 47, 71, 75, 82) search 82 True delete 82 search 82 False Tree(15, 21, 25, 37, 47, 71, 75) search 37 True delete 37 search 37 False Tree(15, 21, 25, 47, 71, 75) search 15 True delete 15 search 15 False Tree(21, 25, 47, 71, 75) search 7 False delete 7 search 7 False Tree(21, 25, 47, 71, 75) search 93 False delete 93 search 93 False Tree(21, 25, 47, 71, 75) search 75 True delete 75 search 75 False Tree(21, 25, 47, 71) search 21 True delete 21 search 21 False Tree(25, 47, 71) search 71 True delete 71 search 71 False Tree(25, 47) search 47 True delete 47 search 47 False Tree(25) search 25 True delete 25 search 25 False Tree()
スタートからゴールまでの経路を求めるプログラムです。詳しい説明は拙作のページ「Algorithms with Python: 集合、グラフ、経路の探索」をお読みくださいませ。
1───3───5 /│ │ 0 │ │ \│ │ 2───4───6 経路図
リスト : 経路の探索 (keiro.py) # 隣接リスト adjacent = ((1, 2), # 0 (0, 2, 3), # 1 (0, 1, 4), # 2 (1, 4, 5), # 3 (2, 3, 6), # 4 (3,), # 5 (1,)) # 6 # 深さ優先探索 def depth_first_search(goal, path): p = path[-1] if p == goal: print(path) else: for x in adjacent[p]: if x in path: continue path.append(x) depth_first_search(goal, path) path.pop() print("----- depth first search -----") depth_first_search(6, [0]) # 幅優先探索 def breadth_first_search(start, goal): que = [[start]] while len(que) > 0: path = que.pop(0) p = path[-1] if p == goal: print(path) else: for x in adjacent[p]: if x in path: continue que.append(path + [x]) print("----- breadth first search -----") breadth_first_search(0, 6) # 反復深化 def iterative_deepening_search(start, goal): def dfs(goal, path, limit): if len(path) == limit: if path[-1] == goal: print(path) else: for x in adjacent[path[-1]]: if x in path: continue path.append(x) dfs(goal, path, limit) path.pop() for limit in range(1, 7): print("-----", limit, "-----") dfs(goal, [start], limit) print("----- iterative deepening search -----") iterative_deepening_search(0, 6)
----- depth first search ----- [0, 1, 2, 4, 6] [0, 1, 3, 4, 6] [0, 2, 1, 3, 4, 6] [0, 2, 4, 6] ----- breadth first search ----- [0, 2, 4, 6] [0, 1, 2, 4, 6] [0, 1, 3, 4, 6] [0, 2, 1, 3, 4, 6] ----- iterative deepening search ----- ----- 1 ----- ----- 2 ----- ----- 3 ----- ----- 4 ----- [0, 2, 4, 6] ----- 5 ----- [0, 1, 2, 4, 6] [0, 1, 3, 4, 6] ----- 6 ----- [0, 2, 1, 3, 4, 6]
簡単なパズルの解法プログラムです。パズルの説明は以下のページをお読みくださいませ。
●───●───● 0───1───2 │\ /│\ /│ │\ /│\ /│ │ ● │ ● │ │ 3 │ 4 │ │/ \│/ \│ │/ \│/ \│ ●───○───● 5───6───7 │\ /│\ /│ │\ /│\ /│ │ ● │ ● │ │ 8 │ 9 │ │/ \│/ \│ │/ \│/ \│ ●───●───● 10───11───12 (1) Hoppers (2) 座標 図 : ペグ・ソリティア (Hoppers)
# # sample03.py : 簡単なプログラム (3) # # Copyright (C) 2018 Makoto Hiroi # # # 小町算 # def calc_expr(expr): n = expr[0] for i in range(1, len(expr), 2): if expr[i] == '+': n += expr[i + 1] else: n -= expr[i + 1] return n def print_expr(expr): for x in expr: print('{} '.format(x), end='') print('= 100') def komachi(n, expr): if n == 10: if calc_expr(expr) == 100: print_expr(expr) else: komachi(n + 1, expr + ['+', n]) komachi(n + 1, expr + ['-', n]) komachi(n + 1, expr[:-1] + [expr[-1] * 10 + n]) print("----- komachi -----") komachi(2, [1]) # # N Queens Problem # def nqueens(n): def attack(q, qs): d = 1 for i in range(len(qs), 0, -1): if q + d == qs[i - 1] or q - d == qs[i - 1]: return True d += 1 return False def nqueens_sub(qs): if len(qs) == n: print(qs) else: for x in range(1, n + 1): if x in qs or attack(x, qs): continue qs.append(x) nqueens_sub(qs) qs.pop() nqueens_sub([]) print("----- N Queens Problem -----") for x in range(4, 9): nqueens(x) # # ナンバープレース # # 盤面の表示 def print_board(board): for xs in board: for n in xs: print(n, end = ' ') print('') # 条件のチェック def check_number(x, y, n, board): for i in range(0, 9): if board[i][y] == n or board[x][i] == n: return False x1 = (x // 3) * 3 y1 = (y // 3) * 3 for i in range(0, 3): for j in range(0, 3): if board[x1 + i][y1 + j] == n: return False return True # ナンバープレースの解法 def numplace(x, y, board): if y >= 9: print_board(board) elif x >= 9: numplace(0, y + 1, board) elif board[x][y]: numplace(x + 1, y, board) else: for n in range(1, 10): if not check_number(x, y, n, board): continue board[x][y] = n numplace(x + 1, y, board) board[x][y] = 0 # 問題 (出典: 数独 - Wikipedia の問題例) sudoku_board = [ [5, 3, 0, 0, 7, 0, 0, 0, 0], [6, 0, 0, 1, 9, 5, 0, 0, 0], [0, 9, 8, 0, 0, 0, 0, 6, 0], [8, 0, 0, 0, 6, 0, 0, 0, 3], [4, 0, 0, 8, 0, 3, 0, 0, 1], [7, 0, 0, 0, 2, 0, 0, 0, 6], [0, 6, 0, 0, 0, 0, 2, 8, 0], [0, 0, 0, 4, 1, 9, 0, 0, 5], [0, 0, 0, 0, 8, 0, 0, 7, 9]] print('----- number place -----') print_board(sudoku_board) print('--------------------') numplace(0, 0, sudoku_board) # # マスターマインド # import itertools # bulls を数える def count_bulls(xs, ys): c = 0 for i in range(0, 4): if xs[i] == ys[i]: c += 1 return c # 同じ数の個数を数える def count_same_numbers(xs, ys): c = 0 for x in xs: if x in ys: c += 1 return c # 今までの質問と矛盾がないか def check_query(code, qs): for q in qs: b = count_bulls(code, q[0]) c = count_same_numbers(code, q[0]) - b if b != q[1] or c != q[2]: return False return True # マスターマインドの解法 def master_mind(answer): query_table = [] for code in itertools.permutations(range(0, 10), 4): if check_query(code, query_table): b = count_bulls(code, answer) c = count_same_numbers(code, answer) - b query_table.append((code, b, c)) print('{}: {}, bulls = {}, cows = {}'.format(len(query_table), code, b, c)) if b == 4: print('Good Job!!!') break print('----- master mind -----') master_mind([9,8,7,6]) master_mind([9,4,3,1]) # # 水差し問題 # MAX_A = 8 MAX_B = 5 # A を空にする def move1(a, b): return 0, b # A を満たす def move2(a, b): return MAX_A, b # A -> B def move3(a, b): c = MAX_B - b if c >= a: return 0, a + b else: return a - c, b + c # B を空にする def move4(a, b): return a, 0 # B を満杯にする def move5(a, b): return a, MAX_B # B -> A def move6(a, b): c = MAX_A - a if c >= b: return a + b, 0 else: return a + c, b - c def water_jug(g): que = [[(0, 0)]] while len(que) > 0: moves = que.pop(0) a, b = moves[-1] if a == g or b == g: print(moves) break for move_func in [move1, move2, move3, move4, move5, move6]: new_state = move_func(a, b) if new_state in moves: continue que.append(moves + [new_state]) print('----- water jug -----') water_jug(4) water_jug(1) # # 農夫と山羊と狼とキャベツの問題 # def check_side(side): if 'wolf' in side and 'goat' in side: return False if 'goat' in side and 'cabbage' in side: return False return True def move_boat(src, dst): src1 = src.difference({'farmer'}) if check_side(src1): yield src1, dst.union({'farmer'}) for x in ['goat', 'wolf', 'cabbage']: if x not in src: continue xs = {'farmer', x} src1 = src.difference(xs) if check_side(src1): yield src1, dst.union(xs) def farmer(): all = {'farmer', 'goat', 'wolf', 'cabbage'} que = [[(all, set())]] while len(que) > 0: move = que.pop(0) left, right = move[-1] if right == all: for xs in move: print(xs) break if 'farmer' in left: # left -> right for state in move_boat(left, right): if state not in move: que.append(move + [state]) else: # right -> right for r, l in move_boat(right, left): state = l, r if state not in move: que.append(move + [state]) print('----- farmer -----') farmer() # # ペグ・ソリティア (Hoppers) # # 定数 SIZE = 13 HOLE = 6 MAX_JUMP = 11 # 跳び先表 (del, to) jump_table = ( ((1, 2), (3, 6), (5, 10)), ((3, 5), (6, 11), (4, 7)), ((1, 0), (4, 6), (7, 12)), ((6, 9),), ((6, 8),), ((3, 1), (6, 7), (8, 11)), ((3, 0), (4, 2), (8, 10), (9, 12)), ((4, 1), (6, 5), (9, 11)), ((6, 4),), ((6, 3),), ((5, 0), (8, 6), (11, 12)), ((8, 5), (6, 1), (9, 7)), ((11, 10), (9, 6), (7, 2)) ) def print_move(moves): print('[{}, '.format(moves[0][0]), end='') p = moves[0][1] for a, b in moves[1:]: if a == p: print('{}, '.format(p), end='') else: print('{}][{}, '.format(p, a), end='') p = b print('{}]'.format(p)) def move_peg(board, from_, del_, to): board[from_] = 0 board[del_] = 0 board[to] = 1 def restore_peg(board, from_, del_, to): board[from_] = 1 board[del_] = 1 board[to] = 0 def hoppers(): cnt = 0 def ids(board, jc, limit, moves): nonlocal cnt if jc > limit: return if len(moves) == MAX_JUMP: if board[HOLE]: print_move(moves) cnt += 1 else: for from_ in range(0, SIZE): if not board[from_]: continue for del_, to in jump_table[from_]: if not board[del_] or board[to]: continue if moves[-1][1] == from_: new_jc = jc else: new_jc = jc + 1 moves.append((from_, to)) move_peg(board, from_, del_, to) ids(board, new_jc, limit, moves) moves.pop() restore_peg(board, from_, del_, to) # 初手を 0 -> 6 に限定 board = [1] * SIZE board[0] = 0 board[3] = 0 for limit in range(2, MAX_JUMP + 1): print("-----", limit, "-----") ids(board, 1, limit, [(0, 6)]) if cnt > 0: print(cnt) break print('----- Hoppers -----') hoppers() # # 三目並べ # # 定数 MARU = 1 BATU = -1 FREE = 0 MARU_WIN = 1 BATU_WIN = -1 DRAW = 0 MAX_VALUE = 2 MIN_VALUE = -2 # 直線 line = ( (0, 1, 2), (3, 4, 5), (6, 7, 8), (0, 3, 6), (1, 4, 7), (2, 5, 8), (0, 4, 8), (2, 4, 6) ) # 勝敗の判定 def check_winner(board): for a, b, c in line: piece = board[a] if piece != FREE and piece == board[b] == board[c]: if piece == MARU: return MARU_WIN else: return BATU_WIN return DRAW # 先手の指し手 def think_maru(board, n): value = MIN_VALUE for i in range(len(board)): if board[i] != FREE: continue board[i] = MARU v = check_winner(board) if v == DRAW and n < len(board) - 1: v = think_batu(board, n + 1) if v > value: value = v board[i] = FREE return value # 後手の指し手 def think_batu(board, n): value = MAX_VALUE for i in range(len(board)): if board[i] != FREE: continue board[i] = BATU v = check_winner(board) if v == DRAW and n < len(board) - 1: v = think_maru(board, n + 1) if v < value: value = v board[i] = FREE return value def tictactoe(): board = [FREE] * 9 for i in range(9): board[i] = MARU v = think_batu(board, 1) print(i, 'value = ', v) board[i] = FREE print('----- tic tac toe -----') tictactoe()
----- komachi ----- 1 + 2 + 3 - 4 + 5 + 6 + 78 + 9 = 100 1 + 2 + 34 - 5 + 67 - 8 + 9 = 100 1 + 23 - 4 + 5 + 6 + 78 - 9 = 100 1 + 23 - 4 + 56 + 7 + 8 + 9 = 100 12 + 3 + 4 + 5 - 6 - 7 + 89 = 100 12 + 3 - 4 + 5 + 67 + 8 + 9 = 100 12 - 3 - 4 + 5 - 6 + 7 + 89 = 100 123 + 4 - 5 + 67 - 89 = 100 123 + 45 - 67 + 8 - 9 = 100 123 - 4 - 5 - 6 - 7 + 8 - 9 = 100 123 - 45 - 67 + 89 = 100 ----- N Queens Problem ----- [2, 4, 1, 3] [3, 1, 4, 2] [1, 3, 5, 2, 4] [1, 4, 2, 5, 3] [2, 4, 1, 3, 5] [2, 5, 3, 1, 4] [3, 1, 4, 2, 5] [3, 5, 2, 4, 1] [4, 1, 3, 5, 2] [4, 2, 5, 3, 1] [5, 2, 4, 1, 3] [5, 3, 1, 4, 2] [2, 4, 6, 1, 3, 5] [3, 6, 2, 5, 1, 4] [4, 1, 5, 2, 6, 3] [5, 3, 1, 6, 4, 2] [1, 3, 5, 7, 2, 4, 6] [1, 4, 7, 3, 6, 2, 5] [1, 5, 2, 6, 3, 7, 4] ・・・省略・・・ [7, 3, 6, 2, 5, 1, 4] [7, 4, 1, 5, 2, 6, 3] [7, 5, 3, 1, 6, 4, 2] [1, 5, 8, 6, 3, 7, 2, 4] [1, 6, 8, 3, 7, 4, 2, 5] [1, 7, 4, 6, 8, 2, 5, 3] ・・・省略・・・ [8, 2, 5, 3, 1, 7, 4, 6] [8, 3, 1, 6, 2, 5, 7, 4] [8, 4, 1, 3, 6, 2, 7, 5] ----- number place ----- 5 3 0 0 7 0 0 0 0 6 0 0 1 9 5 0 0 0 0 9 8 0 0 0 0 6 0 8 0 0 0 6 0 0 0 3 4 0 0 8 0 3 0 0 1 7 0 0 0 2 0 0 0 6 0 6 0 0 0 0 2 8 0 0 0 0 4 1 9 0 0 5 0 0 0 0 8 0 0 7 9 -------------------- 5 3 4 6 7 8 9 1 2 6 7 2 1 9 5 3 4 8 1 9 8 3 4 2 5 6 7 8 5 9 7 6 1 4 2 3 4 2 6 8 5 3 7 9 1 7 1 3 9 2 4 8 5 6 9 6 1 5 3 7 2 8 4 2 8 7 4 1 9 6 3 5 3 4 5 2 8 6 1 7 9 ----- master mind ----- 1: (0, 1, 2, 3), bulls = 0, cows = 0 2: (4, 5, 6, 7), bulls = 0, cows = 2 3: (5, 4, 8, 9), bulls = 0, cows = 2 4: (6, 7, 9, 8), bulls = 0, cows = 4 5: (8, 9, 7, 6), bulls = 2, cows = 2 6: (9, 8, 7, 6), bulls = 4, cows = 0 Good Job!!! 1: (0, 1, 2, 3), bulls = 0, cows = 2 2: (1, 0, 4, 5), bulls = 0, cows = 2 3: (2, 3, 5, 4), bulls = 0, cows = 2 4: (3, 4, 0, 6), bulls = 1, cows = 1 5: (3, 5, 6, 1), bulls = 1, cows = 1 6: (6, 5, 0, 2), bulls = 0, cows = 0 7: (7, 4, 3, 1), bulls = 3, cows = 0 8: (8, 4, 3, 1), bulls = 3, cows = 0 9: (9, 4, 3, 1), bulls = 4, cows = 0 Good Job!!! ----- water jug ----- [(0, 0), (0, 5), (5, 0), (5, 5), (8, 2), (0, 2), (2, 0), (2, 5), (7, 0), (7, 5), (8, 4)] [(0, 0), (8, 0), (3, 5), (3, 0), (0, 3), (8, 3), (6, 5), (6, 0), (1, 5)] ----- farmer ----- ({'wolf', 'farmer', 'goat', 'cabbage'}, set()) ({'wolf', 'cabbage'}, {'farmer', 'goat'}) ({'wolf', 'farmer', 'cabbage'}, {'goat'}) ({'cabbage'}, {'wolf', 'farmer', 'goat'}) ({'farmer', 'cabbage', 'goat'}, {'wolf'}) ({'goat'}, {'wolf', 'farmer', 'cabbage'}) ({'farmer', 'goat'}, {'wolf', 'cabbage'}) (set(), {'wolf', 'farmer', 'cabbage', 'goat'}) ----- Hoppers ----- ----- 2 ----- ----- 3 ----- ----- 4 ----- ----- 5 ----- ----- 6 ----- ----- 7 ----- [0, 6][9, 3][2, 0, 6][11, 1][10, 0, 2, 6][8, 4][12, 2, 6] [0, 6][9, 3][2, 0, 6][11, 1][10, 6][4, 8][12, 2, 0, 10, 6] [0, 6][9, 3][2, 0, 6][11, 1][12, 2, 6][8, 4][10, 0, 2, 6] [0, 6][9, 3][2, 6][8, 4][10, 0, 2, 6][7, 5][12, 10, 0, 6] [0, 6][9, 3][2, 6][8, 4][10, 0, 2, 6][11, 1][12, 2, 0, 6] [0, 6][9, 3][2, 6][8, 4][10, 0, 6][7, 5][12, 10, 0, 2, 6] [0, 6][9, 3][2, 6][8, 4][12, 2, 0, 6][5, 7][10, 12, 2, 6] [0, 6][9, 3][2, 6][8, 4][12, 2, 0, 6][11, 1][10, 0, 2, 6] [0, 6][9, 3][2, 6][8, 4][12, 2, 6][5, 7][10, 12, 2, 0, 6] [0, 6][9, 3][10, 0, 6][7, 5][2, 0, 10, 6][4, 8][12, 10, 6] [0, 6][9, 3][10, 0, 6][7, 5][2, 6][8, 4][12, 10, 0, 2, 6] [0, 6][9, 3][10, 0, 6][7, 5][12, 10, 6][4, 8][2, 0, 10, 6] [0, 6][9, 3][10, 6][4, 8][2, 0, 6][11, 1][12, 2, 0, 10, 6] [0, 6][9, 3][10, 6][4, 8][2, 0, 10, 6][7, 5][12, 10, 0, 6] [0, 6][9, 3][10, 6][4, 8][2, 0, 10, 6][11, 1][12, 2, 0, 6] [0, 6][9, 3][10, 6][4, 8][12, 10, 0, 6][1, 11][2, 12, 10, 6] [0, 6][9, 3][10, 6][4, 8][12, 10, 0, 6][7, 5][2, 0, 10, 6] [0, 6][9, 3][10, 6][4, 8][12, 10, 6][1, 11][2, 12, 10, 0, 6] 18 ----- tic tac toe ----- 0 value = 0 1 value = 0 2 value = 0 3 value = 0 4 value = 0 5 value = 0 6 value = 0 7 value = 0 8 value = 0