Skip to content

Commit

Permalink
Merge pull request #40 from miguel-ambrona/ambrona@LAN
Browse files Browse the repository at this point in the history
Switch move notation to LAN
  • Loading branch information
miguel-ambrona authored Jan 24, 2024
2 parents 5cb56f5 + 59dd652 commit ce0d03b
Show file tree
Hide file tree
Showing 5 changed files with 591 additions and 542 deletions.
38 changes: 20 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ For example:
```
// Julio Sunyer, 1923 (The Chess Amateur)
>>> 4k3/8/8/7K/8/8/8/8 b - - >>= r >>= r >>= h#1
g6xRh5 ↶h8xQh5 e8g8 h5h7# 5rk1/8/6K1/7Q/8/8/8/8 w - - 1 2
Kg6xRh5 ↶Rh8xQh5 O-O Qh5-h7# 5rk1/8/6K1/7Q/8/8/8/8 w - - 1 2
nsols 1
// Andrew Buchanan, 2001 (1 Retros mailing list 24th Jan)
Expand Down Expand Up @@ -127,12 +127,12 @@ nsols 1
retractions are shown after the following command:
```
>>> 8/8/8/8/2Q5/k7/1pP5/K7 w - - >>= r >>= legal
b3b2 8/8/8/8/2Q5/kp6/2P5/K7 b - - ? 0
↶c3xPb2 illegal dead 8/8/8/8/2Q5/k1p5/1PP5/K7 b - - ? 0
↶c3xQb2 dead 8/8/8/8/2Q5/k1p5/1QP5/K7 b - - ? 0
↶c3xRb2 dead 8/8/8/8/2Q5/k1p5/1RP5/K7 b - - ? 0
↶c3xBb2 dead 8/8/8/8/2Q5/k1p5/1BP5/K7 b - - ? 0
↶c3xNb2 dead 8/8/8/8/2Q5/k1p5/1NP5/K7 b - - ? 0
b3-b2+ 8/8/8/8/2Q5/kp6/2P5/K7 b - - ? 0
↶c3xPb2+ illegal dead 8/8/8/8/2Q5/k1p5/1PP5/K7 b - - ? 0
↶c3xQb2+ dead 8/8/8/8/2Q5/k1p5/1QP5/K7 b - - ? 0
↶c3xRb2+ dead 8/8/8/8/2Q5/k1p5/1RP5/K7 b - - ? 0
↶c3xBb2+ dead 8/8/8/8/2Q5/k1p5/1BP5/K7 b - - ? 0
↶c3xNb2+ dead 8/8/8/8/2Q5/k1p5/1NP5/K7 b - - ? 0
nsols 1
```
However, 5 of them were labeled as either illegal or dead.
Expand Down Expand Up @@ -170,22 +170,22 @@ nsols 1
For example:
```
>>> k7/8/2K5/8/8/8/8/8 b - - 0 50 >>= r >>= legal
d7xPc6 k7/3K4/2p5/8/8/8/8/8 w - - ? 50
d7xQc6 k7/3K4/2q5/8/8/8/8/8 w - - ? 50
d7xRc6 k7/3K4/2r5/8/8/8/8/8 w - - ? 50
Kd7xPc6 k7/3K4/2p5/8/8/8/8/8 w - - ? 50
Kd7xQc6 k7/3K4/2q5/8/8/8/8/8 w - - ? 50
Kd7xRc6 k7/3K4/2r5/8/8/8/8/8 w - - ? 50
...
nsols 21
```
but
```
>>> k7/8/2K5/8/8/8/8/8 b - - 1 50 >>= r >>= legal
d7c6 dead k7/3K4/8/8/8/8/8/8 w - - 0 50
d5c6 dead k7/8/8/3K4/8/8/8/8 w - - 0 50
b5c6 dead k7/8/8/1K6/8/8/8/8 w - - 0 50
c7c6 zombie k7/2K5/8/8/8/8/8/8 w - - 0 50
c5c6 dead k7/8/8/2K5/8/8/8/8 w - - 0 50
d6c6 dead k7/8/3K4/8/8/8/8/8 w - - 0 50
b6c6 zombie k7/8/1K6/8/8/8/8/8 w - - 0 50
Kd7-c6 dead k7/3K4/8/8/8/8/8/8 w - - 0 50
Kd5-c6 dead k7/8/8/3K4/8/8/8/8 w - - 0 50
Kb5-c6 dead k7/8/8/1K6/8/8/8/8 w - - 0 50
Kc7-c6 zombie k7/2K5/8/8/8/8/8/8 w - - 0 50
Kc5-c6 dead k7/8/8/2K5/8/8/8/8 w - - 0 50
Kd6-c6 dead k7/8/3K4/8/8/8/8/8 w - - 0 50
Kb6-c6 zombie k7/8/1K6/8/8/8/8/8 w - - 0 50
nsols 0
```

Expand All @@ -197,7 +197,7 @@ nsols 1
term `half-duplex` next to the specification. For example:
```
>>> 8/8/2B5/5Q2/8/4p2P/4k2K/8 w - - >>= h#3 half-duplex
f5b1 e2f2 c6h1 e3e2 b1f1 e2f1n# 8/8/8/8/8/7P/4pk1K/5Q1B b - - 1 3
Qf5-b1 Ke2-f2 Bc6-h1 e3-e2 Qb1-f1+ e2xf1=N# 8/8/8/8/8/7P/4pk1K/5Q1B b - - 1 3
nsols 1
```
Furthermore, if the term `duplex` is specified, both WTM and BTM positions
Expand All @@ -213,6 +213,8 @@ nsols 1

- You can disable the progress bar with `--no-progress-bar`.

- Use `--uci` to display moves in UCI notation (the default is
[LAN](https://en.wikipedia.org/wiki/Algebraic_notation_(chess)#Long_algebraic_notation)).

## Feedback

Expand Down
43 changes: 36 additions & 7 deletions src/deadpos.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

PROGRESS_BAR = not "--no-progress-bar" in sys.argv
SOLVER_ARGS = ["--progress-bar"] if PROGRESS_BAR else []
UCI_NOTATION = "--uci" in sys.argv

CPP_SOLVER = Popen(["./solver.exe"] + SOLVER_ARGS, stdout=PIPE, stdin=PIPE, stderr=STDOUT)
CPP_SOLVER.stdout.readline().strip().decode("utf-8")
Expand Down Expand Up @@ -183,7 +184,7 @@ def legal(pos):
return [pos]

def solver_call(cmd, pos, progress_bar, flush):
global CPP_SOLVER, PYTHON_SOLVER
global CPP_SOLVER, PYTHON_SOLVER, UCI_NOTATION

solver = CPP_SOLVER if "#" in cmd or "--fast" in sys.argv else PYTHON_SOLVER

Expand Down Expand Up @@ -213,13 +214,18 @@ def solver_call(cmd, pos, progress_bar, flush):

for i in range(len(moves)):
try:
move_added = False
m = moves[i].replace("#", "")
m_str = str(moves[i]) if UCI_NOTATION else b.lan(b.parse_uci(m))
history += [m_str]
move_added = True
b.push_uci(moves[i])
history += [moves[i]]
if moves[i+1] != "DP" and not b.is_stalemate() and is_dead(b.fen()):
history += ["dead"] + ["(" + explain_dead(b) + ")"]
break
except:
history += moves[i:]
if not move_added:
history += moves[i:]
break

if not "dead" in history and cmd[:2] == "h=" and cmd[:5] != "h=0.5":
Expand Down Expand Up @@ -286,20 +292,43 @@ def solve(cmd, positions, flush):
return (all_solutions, len(all_solutions))

def backwards(pos):
global UCI_NOTATION

retractions = []
fen = pos.fen()
if not UCI_NOTATION:
board = chess.Board(fen.replace("?", "0"))
for (retracted_fen, retraction) in retract(fen):
history_token = (RETRACTION_SYMBOL + retraction.strip(), retracted_fen)
retraction_str = retraction.strip()
if not UCI_NOTATION:
retraction_str = retraction_str.replace("prom", "").replace("ep", "")
source = retraction_str[:2]
target = retraction_str[-2:]
prom = ""
if "prom" in retraction:
prom = str(board.piece_at(chess.parse_square(target))).lower()
retracted_board = chess.Board(retracted_fen.replace("?", "0"))
move = retracted_board.parse_uci(source + target + prom)
retraction_str = retracted_board.lan(move)
if "ep" in retraction:
retraction_str += "ep"
if "x" in retraction:
captured = str(retracted_board.piece_at(chess.parse_square(target))).upper()
retraction_str = retraction_str.replace("x", "x" + captured)
history_token = (RETRACTION_SYMBOL + retraction_str, retracted_fen)
new_pos = Position(retracted_fen, pos.history + [history_token])
retractions.append(new_pos)
return retractions

def forwards(pos):
global UCI_NOTATION

positions = []
board = chess.Board(pos.fen().replace("?", "0"))
for m in board.legal_moves:
m_str = str(m) if UCI_NOTATION else board.lan(m)
board.push(m)
history_token = (str(m), board.fen())
history_token = (m_str, board.fen())
new_pos = Position(board.fen(), pos.history + [history_token])
positions.append(new_pos)
board.pop()
Expand Down Expand Up @@ -419,8 +448,8 @@ def is_solve_cmd(cmd):
# This is a bit hacky, but does the job for now
return "#" in cmd or cmd[0] == "h"

flush = "plain" if is_solve_cmd(cmds[-1]) else None
if cmds[-1] == "legal" and len(cmds) > 1 and is_solve_cmd(cmds[-2]):
flush = "plain" if len(cmds) > 0 and is_solve_cmd(cmds[-1]) else None
if len(cmds) > 1 and cmds[-1] == "legal" and is_solve_cmd(cmds[-2]):
flush = "with-legal"

for cmd in cmds:
Expand Down
30 changes: 24 additions & 6 deletions src/solver.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ def is_zombie(fen, depth = 1):
return True

def explain_dead(board, depth = 10):

UCI_NOTATION = "--uci" in sys.argv
INTERRUPTED_MSG = "explanation interrupted for being too long"

if board.is_stalemate():
Expand All @@ -148,16 +148,20 @@ def explain_dead(board, depth = 10):

legal_moves = [m for m in board.legal_moves]

board.push(legal_moves[0])
explanation = str(legal_moves[0])
m = legal_moves[0]
explanation = str(m) if UCI_NOTATION else board.lan(m)
board.push(m)
rest_m1_explanation = explain_dead(board, depth - 1)
if rest_m1_explanation == "=":
explanation += "="
board.pop()

for m in legal_moves[1:]:
m_str = str(m) if UCI_NOTATION else board.lan(m)
board.push(m)
explanation += " (" + str(m) + " " + explain_dead(board, depth - 1) + ")"
explanation_after_m = explain_dead(board, depth - 1)
token = " " if explanation_after_m != "=" else ""
explanation += " (" + m_str + token + explanation_after_m + ")"
board.pop()

if rest_m1_explanation != "=":
Expand All @@ -174,6 +178,8 @@ def explain_dead(board, depth = 10):
def explain_alive(fen, depth = 10):
global CHA_MIN

UCI_NOTATION = "--uci" in sys.argv

inp = (fen + " white\n").encode("utf-8")
CHA.stdin.write(inp)
CHA.stdin.flush()
Expand All @@ -197,10 +203,22 @@ def explain_alive(fen, depth = 10):
len_black = 0 if not mate_with_black else len(mate_with_black)

if 0 < len_white < len_black or len_black == 0:
return mate_with_white
line = mate_with_white

else:
line = mate_with_black

if UCI_NOTATION:
return line

else:
return mate_with_black
output = ["living alternative"]
board = chess.Board(fen)
for m in line.split(" ")[2:]:
m = m.replace("#", "")
output.append(board.lan(board.parse_uci(m)))
board.push_uci(m)
return " ".join(output)


(STALEMATE, DEAD, DRAW) = (0, 1, 2)
Expand Down
Loading

0 comments on commit ce0d03b

Please sign in to comment.