pingv4 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.
@@ -0,0 +1,15 @@
1
+ *.so
2
+
3
+ # Python-generated files
4
+ __pycache__/
5
+ *.py[oc]
6
+ build/
7
+ dist/
8
+ wheels/
9
+ *.egg-info
10
+
11
+ # Virtual environments
12
+ .venv
13
+
14
+ # Rust
15
+ target/
@@ -0,0 +1 @@
1
+ 3.13
@@ -0,0 +1,180 @@
1
+ # This file is automatically @generated by Cargo.
2
+ # It is not intended for manual editing.
3
+ version = 4
4
+
5
+ [[package]]
6
+ name = "autocfg"
7
+ version = "1.5.0"
8
+ source = "registry+https://github.com/rust-lang/crates.io-index"
9
+ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
10
+
11
+ [[package]]
12
+ name = "cfg-if"
13
+ version = "1.0.4"
14
+ source = "registry+https://github.com/rust-lang/crates.io-index"
15
+ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
16
+
17
+ [[package]]
18
+ name = "heck"
19
+ version = "0.5.0"
20
+ source = "registry+https://github.com/rust-lang/crates.io-index"
21
+ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
22
+
23
+ [[package]]
24
+ name = "indoc"
25
+ version = "2.0.7"
26
+ source = "registry+https://github.com/rust-lang/crates.io-index"
27
+ checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706"
28
+ dependencies = [
29
+ "rustversion",
30
+ ]
31
+
32
+ [[package]]
33
+ name = "libc"
34
+ version = "0.2.180"
35
+ source = "registry+https://github.com/rust-lang/crates.io-index"
36
+ checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc"
37
+
38
+ [[package]]
39
+ name = "memoffset"
40
+ version = "0.9.1"
41
+ source = "registry+https://github.com/rust-lang/crates.io-index"
42
+ checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
43
+ dependencies = [
44
+ "autocfg",
45
+ ]
46
+
47
+ [[package]]
48
+ name = "once_cell"
49
+ version = "1.21.3"
50
+ source = "registry+https://github.com/rust-lang/crates.io-index"
51
+ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
52
+
53
+ [[package]]
54
+ name = "pingv4"
55
+ version = "0.1.0"
56
+ dependencies = [
57
+ "pyo3",
58
+ ]
59
+
60
+ [[package]]
61
+ name = "portable-atomic"
62
+ version = "1.13.0"
63
+ source = "registry+https://github.com/rust-lang/crates.io-index"
64
+ checksum = "f89776e4d69bb58bc6993e99ffa1d11f228b839984854c7daeb5d37f87cbe950"
65
+
66
+ [[package]]
67
+ name = "proc-macro2"
68
+ version = "1.0.106"
69
+ source = "registry+https://github.com/rust-lang/crates.io-index"
70
+ checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
71
+ dependencies = [
72
+ "unicode-ident",
73
+ ]
74
+
75
+ [[package]]
76
+ name = "pyo3"
77
+ version = "0.22.6"
78
+ source = "registry+https://github.com/rust-lang/crates.io-index"
79
+ checksum = "f402062616ab18202ae8319da13fa4279883a2b8a9d9f83f20dbade813ce1884"
80
+ dependencies = [
81
+ "cfg-if",
82
+ "indoc",
83
+ "libc",
84
+ "memoffset",
85
+ "once_cell",
86
+ "portable-atomic",
87
+ "pyo3-build-config",
88
+ "pyo3-ffi",
89
+ "pyo3-macros",
90
+ "unindent",
91
+ ]
92
+
93
+ [[package]]
94
+ name = "pyo3-build-config"
95
+ version = "0.22.6"
96
+ source = "registry+https://github.com/rust-lang/crates.io-index"
97
+ checksum = "b14b5775b5ff446dd1056212d778012cbe8a0fbffd368029fd9e25b514479c38"
98
+ dependencies = [
99
+ "once_cell",
100
+ "target-lexicon",
101
+ ]
102
+
103
+ [[package]]
104
+ name = "pyo3-ffi"
105
+ version = "0.22.6"
106
+ source = "registry+https://github.com/rust-lang/crates.io-index"
107
+ checksum = "9ab5bcf04a2cdcbb50c7d6105de943f543f9ed92af55818fd17b660390fc8636"
108
+ dependencies = [
109
+ "libc",
110
+ "pyo3-build-config",
111
+ ]
112
+
113
+ [[package]]
114
+ name = "pyo3-macros"
115
+ version = "0.22.6"
116
+ source = "registry+https://github.com/rust-lang/crates.io-index"
117
+ checksum = "0fd24d897903a9e6d80b968368a34e1525aeb719d568dba8b3d4bfa5dc67d453"
118
+ dependencies = [
119
+ "proc-macro2",
120
+ "pyo3-macros-backend",
121
+ "quote",
122
+ "syn",
123
+ ]
124
+
125
+ [[package]]
126
+ name = "pyo3-macros-backend"
127
+ version = "0.22.6"
128
+ source = "registry+https://github.com/rust-lang/crates.io-index"
129
+ checksum = "36c011a03ba1e50152b4b394b479826cad97e7a21eb52df179cd91ac411cbfbe"
130
+ dependencies = [
131
+ "heck",
132
+ "proc-macro2",
133
+ "pyo3-build-config",
134
+ "quote",
135
+ "syn",
136
+ ]
137
+
138
+ [[package]]
139
+ name = "quote"
140
+ version = "1.0.44"
141
+ source = "registry+https://github.com/rust-lang/crates.io-index"
142
+ checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4"
143
+ dependencies = [
144
+ "proc-macro2",
145
+ ]
146
+
147
+ [[package]]
148
+ name = "rustversion"
149
+ version = "1.0.22"
150
+ source = "registry+https://github.com/rust-lang/crates.io-index"
151
+ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
152
+
153
+ [[package]]
154
+ name = "syn"
155
+ version = "2.0.114"
156
+ source = "registry+https://github.com/rust-lang/crates.io-index"
157
+ checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a"
158
+ dependencies = [
159
+ "proc-macro2",
160
+ "quote",
161
+ "unicode-ident",
162
+ ]
163
+
164
+ [[package]]
165
+ name = "target-lexicon"
166
+ version = "0.12.16"
167
+ source = "registry+https://github.com/rust-lang/crates.io-index"
168
+ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
169
+
170
+ [[package]]
171
+ name = "unicode-ident"
172
+ version = "1.0.22"
173
+ source = "registry+https://github.com/rust-lang/crates.io-index"
174
+ checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
175
+
176
+ [[package]]
177
+ name = "unindent"
178
+ version = "0.2.4"
179
+ source = "registry+https://github.com/rust-lang/crates.io-index"
180
+ checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3"
@@ -0,0 +1,15 @@
1
+ [package]
2
+ name = "pingv4"
3
+ version = "0.1.0"
4
+ edition = "2021"
5
+ readme = "README.md"
6
+
7
+ [lib]
8
+ name = "_core"
9
+ # "cdylib" is necessary to produce a shared library for Python to import from.
10
+ crate-type = ["cdylib"]
11
+
12
+ [dependencies]
13
+ # "extension-module" tells pyo3 we want to build an extension module (skips linking against libpython.so)
14
+ # "abi3-py39" tells pyo3 (and maturin) to build using the stable ABI with minimum Python version 3.9
15
+ pyo3 = { version = "0.22.4", features = ["extension-module", "abi3-py39"] }
pingv4-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Developer Student Club - SNU
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
pingv4-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,355 @@
1
+ Metadata-Version: 2.4
2
+ Name: pingv4
3
+ Version: 0.1.0
4
+ Classifier: Intended Audience :: Developers
5
+ Classifier: Intended Audience :: Education
6
+ Classifier: License :: OSI Approved :: MIT License
7
+ Classifier: Natural Language :: English
8
+ Classifier: Programming Language :: Rust
9
+ Classifier: Programming Language :: Python :: 3.9
10
+ Classifier: Programming Language :: Python :: 3.10
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Classifier: Topic :: Software Development :: Libraries
15
+ Classifier: Typing :: Typed
16
+ Requires-Dist: pydantic>=2.12.5
17
+ Requires-Dist: pygame>=2.6.1
18
+ License-File: LICENSE
19
+ Summary: A high-performance Connect Four library with a graphical game interface and bot framework.
20
+ Author-email: lalitm1004 <lalitm1004@gmail.com>
21
+ License: MIT
22
+ Requires-Python: >=3.9
23
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
24
+ Project-URL: Repository, https://github.com/dscsnu/pingv4
25
+ Project-URL: Homepage, https://github.com/dscsnu/pingv4
26
+ Project-URL: Issues, https://github.com/dscsnu/pingv4/issues
27
+
28
+ # pingv4
29
+
30
+ A high-performance Connect Four library with a graphical game interface and bot framework.
31
+
32
+ ## Installation
33
+
34
+ ```bash
35
+ pip install pingv4
36
+ ```
37
+ > *Note: Python 3.9+ required*
38
+
39
+ ## Quick Start
40
+
41
+ ### Play a Game
42
+
43
+ ```python
44
+ from pingv4 import Connect4Game, MinimaxBot
45
+
46
+ # Human vs Human
47
+ game = Connect4Game()
48
+ game.run()
49
+
50
+ # Human vs Bot
51
+ game = Connect4Game(player1=None, player2=MinimaxBot)
52
+ game.run()
53
+
54
+ # Bot vs Bot
55
+ game = Connect4Game(player1=MinimaxBot, player2=MinimaxBot)
56
+ game.run()
57
+ ```
58
+
59
+ ### Use the Board Directly
60
+
61
+ ```python
62
+ from pingv4 import ConnectFourBoard, CellState
63
+
64
+ board = ConnectFourBoard()
65
+
66
+ # Make moves (returns a new board - immutable!)
67
+ board = board.make_move(3) # Red plays center
68
+ board = board.make_move(3) # Yellow plays center
69
+ board = board.make_move(2) # Red plays left of center
70
+
71
+ # Check game state
72
+ print(board.is_in_progress) # True
73
+ print(board.current_player) # CellState.Yellow
74
+ print(board.get_valid_moves()) # [0, 1, 2, 3, 4, 5, 6]
75
+
76
+ # Access cells (column-major: board[col, row])
77
+ print(board[3, 0]) # CellState.Red
78
+ print(board[3, 1]) # CellState.Yellow
79
+
80
+ # Print the board
81
+ print(board)
82
+ ```
83
+
84
+ ---
85
+
86
+ ## API Reference
87
+
88
+ ### `CellState`
89
+
90
+ An enum representing the state of a cell.
91
+
92
+ ```python
93
+ from pingv4 import CellState
94
+
95
+ CellState.Red # Red player (plays first)
96
+ CellState.Yellow # Yellow player
97
+ ```
98
+
99
+ ---
100
+
101
+ ### `ConnectFourBoard`
102
+
103
+ The core game board class. **Immutable** - all operations return new board instances.
104
+
105
+ #### Creating a Board
106
+
107
+ ```python
108
+ board = ConnectFourBoard() # Creates an empty 6x7 board
109
+ ```
110
+
111
+ #### Properties
112
+
113
+ | Property | Type | Description |
114
+ |----------|------|-------------|
115
+ | `num_rows` | `int` | Number of rows (6) |
116
+ | `num_cols` | `int` | Number of columns (7) |
117
+ | `current_player` | `CellState \| None` | Current player, or `None` if game over |
118
+ | `is_in_progress` | `bool` | `True` if game is still ongoing |
119
+ | `is_victory` | `bool` | `True` if a player has won |
120
+ | `is_draw` | `bool` | `True` if the board is full with no winner |
121
+ | `winner` | `CellState \| None` | The winning player, or `None` |
122
+ | `column_heights` | `list[int]` | Number of pieces in each column |
123
+ | `hash` | `int` | Deterministic hash for the board state |
124
+ | `cell_states` | `list[list[CellState \| None]]` | All cells (column-major) |
125
+
126
+ #### Methods
127
+
128
+ ```python
129
+ # Get valid moves (columns that aren't full)
130
+ moves: list[int] = board.get_valid_moves()
131
+
132
+ # Make a move (returns NEW board)
133
+ new_board = board.make_move(col_idx) # col_idx: 0-6
134
+
135
+ # Access a cell (column-major!)
136
+ cell = board[col, row] # col: 0-6, row: 0-5 (bottom to top)
137
+ ```
138
+
139
+ > ⚠️ **Column-Major Access**: Board indexing is `board[column, row]`, not `board[row, column]`.
140
+
141
+ #### Example: Game Loop
142
+
143
+ ```python
144
+ from pingv4 import ConnectFourBoard
145
+
146
+ board = ConnectFourBoard()
147
+
148
+ while board.is_in_progress:
149
+ move = int(input(f"{board.current_player}'s turn. Column (0-6): "))
150
+ if move in board.get_valid_moves():
151
+ board = board.make_move(move)
152
+ print(board)
153
+
154
+ if board.is_victory:
155
+ print(f"{board.winner} wins!")
156
+ else:
157
+ print("It's a draw!")
158
+ ```
159
+
160
+ ---
161
+
162
+ ### `Connect4Game`
163
+
164
+ A pygame-based graphical game interface.
165
+
166
+ ```python
167
+ from pingv4 import Connect4Game, GameConfig, MinimaxBot
168
+
169
+ # Basic usage
170
+ game = Connect4Game(
171
+ player1=None, # Human player
172
+ player2=MinimaxBot, # Bot class
173
+ config=GameConfig() # Optional configuration
174
+ )
175
+ game.run()
176
+ ```
177
+
178
+ #### Constructor Parameters
179
+
180
+ | Parameter | Type | Default | Description |
181
+ |-----------|------|---------|-------------|
182
+ | `player1` | `PlayerConfig` | `None` | First player (see below) |
183
+ | `player2` | `PlayerConfig` | `None` | Second player |
184
+ | `config` | `GameConfig` | `GameConfig()` | Game settings |
185
+
186
+ **`PlayerConfig`** can be:
187
+ - `None` — Human player (manual input)
188
+ - Bot class (e.g., `MinimaxBot`, `RandomBot`) — Will be instantiated automatically
189
+
190
+ #### Controls
191
+
192
+ | Key | Action |
193
+ |-----|--------|
194
+ | Click | Place piece (human turn) |
195
+ | `R` | Restart game |
196
+ | `ESC` | Quit |
197
+
198
+ ---
199
+
200
+ ### `GameConfig`
201
+
202
+ Frozen Pydantic model for game configuration.
203
+
204
+ ```python
205
+ from pingv4 import GameConfig
206
+
207
+ config = GameConfig(
208
+ bot_delay_seconds=0.5, # Delay before bot moves (default: 1.0)
209
+ animation_speed=35, # Piece falling speed (default: 25)
210
+ window_width=700, # Window width in pixels
211
+ window_height=700, # Window height in pixels
212
+ cell_size=80, # Size of each cell
213
+ background_color=(30, 30, 40),
214
+ board_color=(0, 80, 180),
215
+ red_color=(220, 50, 50),
216
+ yellow_color=(240, 220, 50),
217
+ )
218
+ ```
219
+
220
+ ---
221
+
222
+ ## Creating Custom Bots
223
+
224
+ Extend `AbstractBot` to create your own Connect Four AI:
225
+
226
+ ```python
227
+ from pingv4 import AbstractBot, ConnectFourBoard, CellState
228
+
229
+ class MyBot(AbstractBot):
230
+ @property
231
+ def strategy_name(self) -> str:
232
+ return "My Custom Bot"
233
+
234
+ @property
235
+ def author_name(self) -> str:
236
+ return "Your Name"
237
+
238
+ @property
239
+ def author_netid(self) -> str:
240
+ return "your_id"
241
+
242
+ def get_move(self, board: ConnectFourBoard) -> int:
243
+ """Return a column index (0-6) for your move."""
244
+ valid_moves = board.get_valid_moves()
245
+
246
+ # Your strategy here!
247
+ # Example: prefer center columns
248
+ for col in [3, 2, 4, 1, 5, 0, 6]:
249
+ if col in valid_moves:
250
+ return col
251
+
252
+ return valid_moves[0]
253
+ ```
254
+
255
+ ### Using Your Bot
256
+
257
+ ```python
258
+ from pingv4 import Connect4Game
259
+
260
+ game = Connect4Game(player1=MyBot, player2=None)
261
+ game.run()
262
+ ```
263
+
264
+ ### Bot Interface
265
+
266
+ Your bot receives a `ConnectFourBoard` and must return a valid column index.
267
+
268
+ ```python
269
+ def get_move(self, board: ConnectFourBoard) -> int:
270
+ # Useful properties:
271
+ board.current_player # Your color (CellState.Red or CellState.Yellow)
272
+ board.get_valid_moves() # List of valid columns
273
+ board.column_heights # How full each column is
274
+ board.hash # For transposition tables
275
+
276
+ # Simulate moves:
277
+ future = board.make_move(col) # Returns new board
278
+
279
+ # Check outcomes:
280
+ future.is_victory # Did someone win?
281
+ future.winner # Who won?
282
+
283
+ return column_index # 0-6
284
+ ```
285
+
286
+ ---
287
+
288
+ ## Built-in Bots
289
+
290
+ ### `RandomBot`
291
+
292
+ Plays random valid moves. Useful for testing.
293
+
294
+ ```python
295
+ from pingv4 import RandomBot
296
+ ```
297
+
298
+ ### `MinimaxBot`
299
+
300
+ Strong AI using minimax with alpha-beta pruning.
301
+
302
+ ```python
303
+ from pingv4 import MinimaxBot
304
+
305
+ # Default depth is 6
306
+ game = Connect4Game(player1=MinimaxBot, player2=MinimaxBot)
307
+ ```
308
+
309
+ Features:
310
+ - Alpha-beta pruning
311
+ - Transposition tables (using `board.hash`)
312
+ - Center-preference move ordering
313
+ - Positional evaluation
314
+
315
+ ---
316
+
317
+ ## Tips for Bot Development
318
+
319
+ ### Use the Hash for Caching
320
+
321
+ The `board.hash` property is highly optimized. Use it for transposition tables:
322
+
323
+ ```python
324
+ cache = {}
325
+
326
+ def evaluate(board):
327
+ if board.hash in cache:
328
+ return cache[board.hash]
329
+
330
+ score = expensive_calculation(board)
331
+ cache[board.hash] = score
332
+ return score
333
+ ```
334
+
335
+ ### Board is Immutable
336
+
337
+ `make_move()` returns a new board. The original is unchanged:
338
+
339
+ ```python
340
+ board1 = ConnectFourBoard()
341
+ board2 = board1.make_move(3)
342
+
343
+ board1[3, 0] # None (unchanged)
344
+ board2[3, 0] # CellState.Red
345
+ ```
346
+
347
+ ### Column-Major Access
348
+
349
+ Remember: it's `board[column, row]`, not `board[row, column]`.
350
+
351
+ ```python
352
+ # Check if column 3 has a red piece at the bottom
353
+ if board[3, 0] == CellState.Red:
354
+ ...
355
+ ```