fisch-chess 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fisch_chess-0.1.0/PKG-INFO +144 -0
- fisch_chess-0.1.0/README.md +137 -0
- fisch_chess-0.1.0/fisch_chess.egg-info/PKG-INFO +144 -0
- fisch_chess-0.1.0/fisch_chess.egg-info/SOURCES.txt +14 -0
- fisch_chess-0.1.0/fisch_chess.egg-info/dependency_links.txt +1 -0
- fisch_chess-0.1.0/fisch_chess.egg-info/entry_points.txt +2 -0
- fisch_chess-0.1.0/fisch_chess.egg-info/top_level.txt +1 -0
- fisch_chess-0.1.0/pyproject.toml +17 -0
- fisch_chess-0.1.0/setup.cfg +4 -0
- fisch_chess-0.1.0/src/__init__.py +0 -0
- fisch_chess-0.1.0/src/__main__.py +0 -0
- fisch_chess-0.1.0/src/board.py +164 -0
- fisch_chess-0.1.0/src/engine.py +275 -0
- fisch_chess-0.1.0/src/main.py +485 -0
- fisch_chess-0.1.0/src/moves.py +132 -0
- fisch_chess-0.1.0/src/pieces.py +31 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fisch-chess
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A lightweight terminal chess game written in Python.
|
|
5
|
+
Requires-Python: >=3.11
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
|
|
8
|
+
# FISCH CHESS
|
|
9
|
+
|
|
10
|
+
> chess built from scratch, for the terminal.
|
|
11
|
+
|
|
12
|
+
This is a lightweight chess game written in Python and played entirely inside the terminal(v1).
|
|
13
|
+
|
|
14
|
+
## ✦ Features
|
|
15
|
+
```
|
|
16
|
+
[-] local two-player chess
|
|
17
|
+
[-] simple computer opponent
|
|
18
|
+
[-] legal move validation
|
|
19
|
+
[-] check + checkmate
|
|
20
|
+
[-] castling
|
|
21
|
+
[-] en passant
|
|
22
|
+
[-] pawn promotion
|
|
23
|
+
[-] stalemate + draw detection
|
|
24
|
+
[-] undo / redo
|
|
25
|
+
[-] move history
|
|
26
|
+
[-] FEN loading
|
|
27
|
+
[-] puzzle mode
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
chess logic is implemented inside the project itself insetad of relying on Stockish, Lichess, Chess(dot)com or another chess library/service
|
|
31
|
+
|
|
32
|
+
## ✦ Installation
|
|
33
|
+
|
|
34
|
+
You need:
|
|
35
|
+
`Python 3.11+`
|
|
36
|
+
|
|
37
|
+
Clone the repo:
|
|
38
|
+
|
|
39
|
+
> `git clone https://github.com/noxindeed/fisch-chess/`
|
|
40
|
+
> `cd fisch-chess`
|
|
41
|
+
|
|
42
|
+
Optional, but recommended is to run it in a venv
|
|
43
|
+
|
|
44
|
+
## ✦ Run
|
|
45
|
+
|
|
46
|
+
From the project root:
|
|
47
|
+
|
|
48
|
+
`python3 -m src.main`
|
|
49
|
+
|
|
50
|
+
You should get:
|
|
51
|
+
```
|
|
52
|
+
FISCH CHESS
|
|
53
|
+
===========
|
|
54
|
+
|
|
55
|
+
1. New Game (two players, offline)
|
|
56
|
+
2. New Game (vs not so smart AI)
|
|
57
|
+
3. Load Position (FEN)
|
|
58
|
+
4. Puzzle Mode
|
|
59
|
+
5. Quit
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## moves
|
|
63
|
+
|
|
64
|
+
Fisch uses coordinate notation.
|
|
65
|
+
So,
|
|
66
|
+
|
|
67
|
+
`e2e4` means: `e2 → e4`
|
|
68
|
+
|
|
69
|
+
Knight example: `g1f3`
|
|
70
|
+
|
|
71
|
+
Promotion: `e7e8q`
|
|
72
|
+
|
|
73
|
+
Promotion pieces:
|
|
74
|
+
|
|
75
|
+
- q queen
|
|
76
|
+
- r rook
|
|
77
|
+
- b bishop
|
|
78
|
+
- n knight
|
|
79
|
+
|
|
80
|
+
## ✦ Commands
|
|
81
|
+
|
|
82
|
+
While playing:
|
|
83
|
+
|
|
84
|
+
u - undo
|
|
85
|
+
r - redo
|
|
86
|
+
h - move history
|
|
87
|
+
f - show FEN
|
|
88
|
+
n - new game
|
|
89
|
+
q - back to menu
|
|
90
|
+
|
|
91
|
+
## ✦ Project structure
|
|
92
|
+
```
|
|
93
|
+
fisch-chess/
|
|
94
|
+
│
|
|
95
|
+
└── src/
|
|
96
|
+
├── main.py
|
|
97
|
+
├── engine.py
|
|
98
|
+
├── board.py
|
|
99
|
+
├── moves.py
|
|
100
|
+
├── pieces.py
|
|
101
|
+
└── __init__.py
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
- main.py
|
|
105
|
+
|
|
106
|
+
Terminal UI, menus, commands, puzzles and the computer player.
|
|
107
|
+
|
|
108
|
+
- engine.py
|
|
109
|
+
|
|
110
|
+
Game state, legal move filtering, checks, draws, FEN, history and move application.
|
|
111
|
+
|
|
112
|
+
- board.py
|
|
113
|
+
|
|
114
|
+
Board representation, coordinates and attack detection.
|
|
115
|
+
|
|
116
|
+
- moves.py
|
|
117
|
+
|
|
118
|
+
Pseudo-legal move generation for every piece.
|
|
119
|
+
|
|
120
|
+
- pieces.py
|
|
121
|
+
|
|
122
|
+
Piece/color helpers.
|
|
123
|
+
|
|
124
|
+
## ✦ Future plans
|
|
125
|
+
V2
|
|
126
|
+
```
|
|
127
|
+
├── online multiplayer
|
|
128
|
+
├── create / join game codes
|
|
129
|
+
├── real-time moves
|
|
130
|
+
├── reconnecting
|
|
131
|
+
├── resignation
|
|
132
|
+
└── draw offers
|
|
133
|
+
```
|
|
134
|
+
later
|
|
135
|
+
```
|
|
136
|
+
├── rematches
|
|
137
|
+
├── chess clocks
|
|
138
|
+
├── spectators
|
|
139
|
+
├── saved games
|
|
140
|
+
├── better puzzles
|
|
141
|
+
├── stronger AI
|
|
142
|
+
├── game history
|
|
143
|
+
└── web client
|
|
144
|
+
```
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# FISCH CHESS
|
|
2
|
+
|
|
3
|
+
> chess built from scratch, for the terminal.
|
|
4
|
+
|
|
5
|
+
This is a lightweight chess game written in Python and played entirely inside the terminal(v1).
|
|
6
|
+
|
|
7
|
+
## ✦ Features
|
|
8
|
+
```
|
|
9
|
+
[-] local two-player chess
|
|
10
|
+
[-] simple computer opponent
|
|
11
|
+
[-] legal move validation
|
|
12
|
+
[-] check + checkmate
|
|
13
|
+
[-] castling
|
|
14
|
+
[-] en passant
|
|
15
|
+
[-] pawn promotion
|
|
16
|
+
[-] stalemate + draw detection
|
|
17
|
+
[-] undo / redo
|
|
18
|
+
[-] move history
|
|
19
|
+
[-] FEN loading
|
|
20
|
+
[-] puzzle mode
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
chess logic is implemented inside the project itself insetad of relying on Stockish, Lichess, Chess(dot)com or another chess library/service
|
|
24
|
+
|
|
25
|
+
## ✦ Installation
|
|
26
|
+
|
|
27
|
+
You need:
|
|
28
|
+
`Python 3.11+`
|
|
29
|
+
|
|
30
|
+
Clone the repo:
|
|
31
|
+
|
|
32
|
+
> `git clone https://github.com/noxindeed/fisch-chess/`
|
|
33
|
+
> `cd fisch-chess`
|
|
34
|
+
|
|
35
|
+
Optional, but recommended is to run it in a venv
|
|
36
|
+
|
|
37
|
+
## ✦ Run
|
|
38
|
+
|
|
39
|
+
From the project root:
|
|
40
|
+
|
|
41
|
+
`python3 -m src.main`
|
|
42
|
+
|
|
43
|
+
You should get:
|
|
44
|
+
```
|
|
45
|
+
FISCH CHESS
|
|
46
|
+
===========
|
|
47
|
+
|
|
48
|
+
1. New Game (two players, offline)
|
|
49
|
+
2. New Game (vs not so smart AI)
|
|
50
|
+
3. Load Position (FEN)
|
|
51
|
+
4. Puzzle Mode
|
|
52
|
+
5. Quit
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## moves
|
|
56
|
+
|
|
57
|
+
Fisch uses coordinate notation.
|
|
58
|
+
So,
|
|
59
|
+
|
|
60
|
+
`e2e4` means: `e2 → e4`
|
|
61
|
+
|
|
62
|
+
Knight example: `g1f3`
|
|
63
|
+
|
|
64
|
+
Promotion: `e7e8q`
|
|
65
|
+
|
|
66
|
+
Promotion pieces:
|
|
67
|
+
|
|
68
|
+
- q queen
|
|
69
|
+
- r rook
|
|
70
|
+
- b bishop
|
|
71
|
+
- n knight
|
|
72
|
+
|
|
73
|
+
## ✦ Commands
|
|
74
|
+
|
|
75
|
+
While playing:
|
|
76
|
+
|
|
77
|
+
u - undo
|
|
78
|
+
r - redo
|
|
79
|
+
h - move history
|
|
80
|
+
f - show FEN
|
|
81
|
+
n - new game
|
|
82
|
+
q - back to menu
|
|
83
|
+
|
|
84
|
+
## ✦ Project structure
|
|
85
|
+
```
|
|
86
|
+
fisch-chess/
|
|
87
|
+
│
|
|
88
|
+
└── src/
|
|
89
|
+
├── main.py
|
|
90
|
+
├── engine.py
|
|
91
|
+
├── board.py
|
|
92
|
+
├── moves.py
|
|
93
|
+
├── pieces.py
|
|
94
|
+
└── __init__.py
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
- main.py
|
|
98
|
+
|
|
99
|
+
Terminal UI, menus, commands, puzzles and the computer player.
|
|
100
|
+
|
|
101
|
+
- engine.py
|
|
102
|
+
|
|
103
|
+
Game state, legal move filtering, checks, draws, FEN, history and move application.
|
|
104
|
+
|
|
105
|
+
- board.py
|
|
106
|
+
|
|
107
|
+
Board representation, coordinates and attack detection.
|
|
108
|
+
|
|
109
|
+
- moves.py
|
|
110
|
+
|
|
111
|
+
Pseudo-legal move generation for every piece.
|
|
112
|
+
|
|
113
|
+
- pieces.py
|
|
114
|
+
|
|
115
|
+
Piece/color helpers.
|
|
116
|
+
|
|
117
|
+
## ✦ Future plans
|
|
118
|
+
V2
|
|
119
|
+
```
|
|
120
|
+
├── online multiplayer
|
|
121
|
+
├── create / join game codes
|
|
122
|
+
├── real-time moves
|
|
123
|
+
├── reconnecting
|
|
124
|
+
├── resignation
|
|
125
|
+
└── draw offers
|
|
126
|
+
```
|
|
127
|
+
later
|
|
128
|
+
```
|
|
129
|
+
├── rematches
|
|
130
|
+
├── chess clocks
|
|
131
|
+
├── spectators
|
|
132
|
+
├── saved games
|
|
133
|
+
├── better puzzles
|
|
134
|
+
├── stronger AI
|
|
135
|
+
├── game history
|
|
136
|
+
└── web client
|
|
137
|
+
```
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fisch-chess
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A lightweight terminal chess game written in Python.
|
|
5
|
+
Requires-Python: >=3.11
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
|
|
8
|
+
# FISCH CHESS
|
|
9
|
+
|
|
10
|
+
> chess built from scratch, for the terminal.
|
|
11
|
+
|
|
12
|
+
This is a lightweight chess game written in Python and played entirely inside the terminal(v1).
|
|
13
|
+
|
|
14
|
+
## ✦ Features
|
|
15
|
+
```
|
|
16
|
+
[-] local two-player chess
|
|
17
|
+
[-] simple computer opponent
|
|
18
|
+
[-] legal move validation
|
|
19
|
+
[-] check + checkmate
|
|
20
|
+
[-] castling
|
|
21
|
+
[-] en passant
|
|
22
|
+
[-] pawn promotion
|
|
23
|
+
[-] stalemate + draw detection
|
|
24
|
+
[-] undo / redo
|
|
25
|
+
[-] move history
|
|
26
|
+
[-] FEN loading
|
|
27
|
+
[-] puzzle mode
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
chess logic is implemented inside the project itself insetad of relying on Stockish, Lichess, Chess(dot)com or another chess library/service
|
|
31
|
+
|
|
32
|
+
## ✦ Installation
|
|
33
|
+
|
|
34
|
+
You need:
|
|
35
|
+
`Python 3.11+`
|
|
36
|
+
|
|
37
|
+
Clone the repo:
|
|
38
|
+
|
|
39
|
+
> `git clone https://github.com/noxindeed/fisch-chess/`
|
|
40
|
+
> `cd fisch-chess`
|
|
41
|
+
|
|
42
|
+
Optional, but recommended is to run it in a venv
|
|
43
|
+
|
|
44
|
+
## ✦ Run
|
|
45
|
+
|
|
46
|
+
From the project root:
|
|
47
|
+
|
|
48
|
+
`python3 -m src.main`
|
|
49
|
+
|
|
50
|
+
You should get:
|
|
51
|
+
```
|
|
52
|
+
FISCH CHESS
|
|
53
|
+
===========
|
|
54
|
+
|
|
55
|
+
1. New Game (two players, offline)
|
|
56
|
+
2. New Game (vs not so smart AI)
|
|
57
|
+
3. Load Position (FEN)
|
|
58
|
+
4. Puzzle Mode
|
|
59
|
+
5. Quit
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## moves
|
|
63
|
+
|
|
64
|
+
Fisch uses coordinate notation.
|
|
65
|
+
So,
|
|
66
|
+
|
|
67
|
+
`e2e4` means: `e2 → e4`
|
|
68
|
+
|
|
69
|
+
Knight example: `g1f3`
|
|
70
|
+
|
|
71
|
+
Promotion: `e7e8q`
|
|
72
|
+
|
|
73
|
+
Promotion pieces:
|
|
74
|
+
|
|
75
|
+
- q queen
|
|
76
|
+
- r rook
|
|
77
|
+
- b bishop
|
|
78
|
+
- n knight
|
|
79
|
+
|
|
80
|
+
## ✦ Commands
|
|
81
|
+
|
|
82
|
+
While playing:
|
|
83
|
+
|
|
84
|
+
u - undo
|
|
85
|
+
r - redo
|
|
86
|
+
h - move history
|
|
87
|
+
f - show FEN
|
|
88
|
+
n - new game
|
|
89
|
+
q - back to menu
|
|
90
|
+
|
|
91
|
+
## ✦ Project structure
|
|
92
|
+
```
|
|
93
|
+
fisch-chess/
|
|
94
|
+
│
|
|
95
|
+
└── src/
|
|
96
|
+
├── main.py
|
|
97
|
+
├── engine.py
|
|
98
|
+
├── board.py
|
|
99
|
+
├── moves.py
|
|
100
|
+
├── pieces.py
|
|
101
|
+
└── __init__.py
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
- main.py
|
|
105
|
+
|
|
106
|
+
Terminal UI, menus, commands, puzzles and the computer player.
|
|
107
|
+
|
|
108
|
+
- engine.py
|
|
109
|
+
|
|
110
|
+
Game state, legal move filtering, checks, draws, FEN, history and move application.
|
|
111
|
+
|
|
112
|
+
- board.py
|
|
113
|
+
|
|
114
|
+
Board representation, coordinates and attack detection.
|
|
115
|
+
|
|
116
|
+
- moves.py
|
|
117
|
+
|
|
118
|
+
Pseudo-legal move generation for every piece.
|
|
119
|
+
|
|
120
|
+
- pieces.py
|
|
121
|
+
|
|
122
|
+
Piece/color helpers.
|
|
123
|
+
|
|
124
|
+
## ✦ Future plans
|
|
125
|
+
V2
|
|
126
|
+
```
|
|
127
|
+
├── online multiplayer
|
|
128
|
+
├── create / join game codes
|
|
129
|
+
├── real-time moves
|
|
130
|
+
├── reconnecting
|
|
131
|
+
├── resignation
|
|
132
|
+
└── draw offers
|
|
133
|
+
```
|
|
134
|
+
later
|
|
135
|
+
```
|
|
136
|
+
├── rematches
|
|
137
|
+
├── chess clocks
|
|
138
|
+
├── spectators
|
|
139
|
+
├── saved games
|
|
140
|
+
├── better puzzles
|
|
141
|
+
├── stronger AI
|
|
142
|
+
├── game history
|
|
143
|
+
└── web client
|
|
144
|
+
```
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
fisch_chess.egg-info/PKG-INFO
|
|
4
|
+
fisch_chess.egg-info/SOURCES.txt
|
|
5
|
+
fisch_chess.egg-info/dependency_links.txt
|
|
6
|
+
fisch_chess.egg-info/entry_points.txt
|
|
7
|
+
fisch_chess.egg-info/top_level.txt
|
|
8
|
+
src/__init__.py
|
|
9
|
+
src/__main__.py
|
|
10
|
+
src/board.py
|
|
11
|
+
src/engine.py
|
|
12
|
+
src/main.py
|
|
13
|
+
src/moves.py
|
|
14
|
+
src/pieces.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
src
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "fisch-chess"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "A lightweight terminal chess game written in Python."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
dependencies = []
|
|
12
|
+
|
|
13
|
+
[project.scripts]
|
|
14
|
+
fisch = "src.main:main"
|
|
15
|
+
|
|
16
|
+
[tool.setuptools]
|
|
17
|
+
packages = ["src"]
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
from .pieces import EMPTY, WHITE, BLACK, color_of, opponent
|
|
2
|
+
|
|
3
|
+
STARTING_FEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
|
|
4
|
+
|
|
5
|
+
# directions and offsets for mvmt
|
|
6
|
+
KNIGHT_OFFSETS = [
|
|
7
|
+
(-2,-1), (-2,1),(-1,-2),(-1,2),(1,-2),(1,2),
|
|
8
|
+
(2,-1),(2,1),
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
KING_OFFSETS = [
|
|
12
|
+
(-1,-1),(-1,0),(-1,1),(0,-1),(0,1),(1,-1),(1,0),(1,1),
|
|
13
|
+
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
BISHOP_DIRECTIONS = [(-1,-1),(-1,1),(1,-1),(1,1)]
|
|
17
|
+
ROOK_DIRECTIONS = [(-1,0),(1,0),(0,-1),(0,1)]
|
|
18
|
+
|
|
19
|
+
QUEEN_DIRECTIONS = BISHOP_DIRECTIONS + ROOK_DIRECTIONS
|
|
20
|
+
|
|
21
|
+
def on_board(row, col):
|
|
22
|
+
return 0 <= row < 8 and 0 <= col < 8
|
|
23
|
+
|
|
24
|
+
def square_name(r,c):
|
|
25
|
+
# converts (r,c) to algebraic names
|
|
26
|
+
return chr(ord("a")+c) + str(8-r)
|
|
27
|
+
|
|
28
|
+
def parse_square(name):
|
|
29
|
+
# converts algebraic names to row,col pair
|
|
30
|
+
if len(name) != 2:
|
|
31
|
+
return None
|
|
32
|
+
|
|
33
|
+
file_ch, rank_ch = name[0], name[1]
|
|
34
|
+
if file_ch < "a" or file_ch > "h" or rank_ch < "1" or rank_ch > "8":
|
|
35
|
+
return None
|
|
36
|
+
return (8-int(rank_ch), ord(file_ch) - ord("a"))
|
|
37
|
+
|
|
38
|
+
class Board:
|
|
39
|
+
def __init__(self):
|
|
40
|
+
self.grid = [[EMPTY]*8 for _ in range(8)]
|
|
41
|
+
|
|
42
|
+
def get(self, row, col):
|
|
43
|
+
return self.grid[row][col]
|
|
44
|
+
|
|
45
|
+
def set(self, row,col,piece):
|
|
46
|
+
self.grid[row][col] = piece
|
|
47
|
+
|
|
48
|
+
def find_king(self, color):
|
|
49
|
+
target = "K" if color == WHITE else "k"
|
|
50
|
+
for row in range(8):
|
|
51
|
+
for col in range(8):
|
|
52
|
+
if self.grid[row][col] == target:
|
|
53
|
+
return (row,col)
|
|
54
|
+
return None
|
|
55
|
+
|
|
56
|
+
# attack detection
|
|
57
|
+
|
|
58
|
+
def is_square_attacked(self, row, col, by_color):
|
|
59
|
+
|
|
60
|
+
# pawns
|
|
61
|
+
pawn = "P" if by_color == WHITE else "p"
|
|
62
|
+
pawn_offset = 1 if by_color == WHITE else -1
|
|
63
|
+
for dc in (-1, 1):
|
|
64
|
+
r,c = row + pawn_offset, col+dc
|
|
65
|
+
if on_board(r, c) and self.grid[r][c] == pawn:
|
|
66
|
+
return True
|
|
67
|
+
|
|
68
|
+
# knights
|
|
69
|
+
knight = "N" if by_color == WHITE else "n"
|
|
70
|
+
for dr, dc in KNIGHT_OFFSETS:
|
|
71
|
+
r, c = row + dr, col + dc
|
|
72
|
+
if on_board(r,c) and self.grid[r][c] == knight:
|
|
73
|
+
return True
|
|
74
|
+
|
|
75
|
+
diagonals = ("B" if by_color == WHITE else "b",
|
|
76
|
+
"Q" if by_color == WHITE else "q")
|
|
77
|
+
straights = ("R" if by_color == WHITE else "r",
|
|
78
|
+
"Q" if by_color == WHITE else "q")
|
|
79
|
+
|
|
80
|
+
#kings
|
|
81
|
+
king = "K" if by_color == WHITE else "k"
|
|
82
|
+
for dr,dc in KING_OFFSETS:
|
|
83
|
+
r,c = row + dr, col + dc
|
|
84
|
+
if on_board(r,c) and self.grid[r][c] == king:
|
|
85
|
+
return True
|
|
86
|
+
|
|
87
|
+
diagonals= (
|
|
88
|
+
"B" if by_color == WHITE else "b",
|
|
89
|
+
"Q" if by_color == WHITE else "q",
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
straights = (
|
|
93
|
+
"R" if by_color == WHITE else "r",
|
|
94
|
+
"Q" if by_color == WHITE else "q",
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
if self._ray_attacks(row, col, BISHOP_DIRECTIONS, diagonals):
|
|
98
|
+
return True
|
|
99
|
+
if self._ray_attacks(row, col, ROOK_DIRECTIONS, straights):
|
|
100
|
+
return True
|
|
101
|
+
return False
|
|
102
|
+
|
|
103
|
+
def _ray_attacks(self, row, col, directions, attackers):
|
|
104
|
+
for dr, dc in directions:
|
|
105
|
+
r,c = row+dr, col+dc
|
|
106
|
+
while on_board(r,c):
|
|
107
|
+
piece = self.grid[r][c]
|
|
108
|
+
if piece != EMPTY:
|
|
109
|
+
if piece in attackers:
|
|
110
|
+
return True
|
|
111
|
+
break
|
|
112
|
+
r += dr
|
|
113
|
+
c += dc
|
|
114
|
+
return False
|
|
115
|
+
|
|
116
|
+
# FEN
|
|
117
|
+
|
|
118
|
+
def to_fen_board(self):
|
|
119
|
+
rows = []
|
|
120
|
+
for r in range(8):
|
|
121
|
+
empty = 0
|
|
122
|
+
out = ""
|
|
123
|
+
for col in range(8):
|
|
124
|
+
piece = self.grid[r][col]
|
|
125
|
+
if piece == EMPTY:
|
|
126
|
+
empty += 1
|
|
127
|
+
else:
|
|
128
|
+
if empty:
|
|
129
|
+
out += str(empty)
|
|
130
|
+
empty = 0
|
|
131
|
+
out += piece
|
|
132
|
+
if empty:
|
|
133
|
+
out += str(empty)
|
|
134
|
+
rows.append(out)
|
|
135
|
+
return "/".join(rows)
|
|
136
|
+
|
|
137
|
+
def load_fen_board(self, placement):
|
|
138
|
+
self.grid = [[EMPTY]*8 for _ in range(8)]
|
|
139
|
+
ranks = placement.split("/")
|
|
140
|
+
|
|
141
|
+
if len(ranks) != 8:
|
|
142
|
+
raise ValueError("FEN board must have 8 ranks")
|
|
143
|
+
for row, rank in enumerate(ranks):
|
|
144
|
+
col = 0
|
|
145
|
+
for ch in rank:
|
|
146
|
+
if ch.isdigit():
|
|
147
|
+
col += int(ch)
|
|
148
|
+
elif ch in "PNBRQKpnbrqk":
|
|
149
|
+
if col >= 8:
|
|
150
|
+
raise ValueError("FEN rank too long")
|
|
151
|
+
self.grid[row][col] = ch
|
|
152
|
+
col += 1
|
|
153
|
+
else:
|
|
154
|
+
raise ValueError(f"Invalid FEN character: {ch!r}")
|
|
155
|
+
if col != 8:
|
|
156
|
+
raise ValueError("FEN rank too short")
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
|