branch: elpa/aidermacs commit 800c10941348b9eec43eb41bf12c2f28271f9409 Author: Mingde (Matthew) Zeng <matthew...@posteo.net> Commit: Mingde (Matthew) Zeng <matthew...@posteo.net>
Update paperwork, remove examples --- aidermacs-backend-comint.el | 6 + aidermacs-backend-vterm.el | 6 + aidermacs-backends.el | 6 + aidermacs-doom.el | 6 + aidermacs-helm.el | 9 +- aidermacs-models.el | 6 + aidermacs-utils.el | 6 + aidermacs.el | 7 +- examples/README.org | 16 -- examples/battleship/README.org | 167 ----------------- examples/battleship/fleet.py | 61 ------ examples/battleship/game.py | 384 -------------------------------------- examples/battleship/test_fleet.py | 29 --- examples/battleship/test_game.py | 42 ----- examples/connect4/README.org | 62 ------ examples/connect4/game.py | 263 -------------------------- examples/connect4/test_game.py | 146 --------------- scratch.aider | 24 --- 18 files changed, 43 insertions(+), 1203 deletions(-) diff --git a/aidermacs-backend-comint.el b/aidermacs-backend-comint.el index 4b744ea4b9..848c739f1a 100644 --- a/aidermacs-backend-comint.el +++ b/aidermacs-backend-comint.el @@ -1,4 +1,10 @@ ;;; aidermacs-backend-comint.el --- Comint backend for aidermacs.el -*- lexical-binding: t; -*- +;; Author: Mingde (Matthew) Zeng <matthew...@posteo.net> +;; Version: 0.5.0 +;; Package-Requires: ((emacs "26.1") (transient "0.3.0")) +;; Keywords: ai emacs agents llm aider ai-pair-programming, convenience, tools +;; URL: https://github.com/MatthewZMD/aidermacs.el +;; Originally inspired by: Kang Tu <tni...@gmail.com> Aider.el ;;; Commentary: ;; Comint backend implementation for aidermacs.el diff --git a/aidermacs-backend-vterm.el b/aidermacs-backend-vterm.el index 801bb2f5c9..c417611247 100644 --- a/aidermacs-backend-vterm.el +++ b/aidermacs-backend-vterm.el @@ -1,4 +1,10 @@ ;;; aidermacs-backend-vterm.el --- VTerm backend for aidermacs.el -*- lexical-binding: t; -*- +;; Author: Mingde (Matthew) Zeng <matthew...@posteo.net> +;; Version: 0.5.0 +;; Package-Requires: ((emacs "26.1") (transient "0.3.0")) +;; Keywords: ai emacs agents llm aider ai-pair-programming, convenience, tools +;; URL: https://github.com/MatthewZMD/aidermacs.el +;; Originally inspired by: Kang Tu <tni...@gmail.com> Aider.el ;;; Commentary: ;; VTerm backend implementation for aidermacs.el diff --git a/aidermacs-backends.el b/aidermacs-backends.el index fbb6fd12de..a36873858d 100644 --- a/aidermacs-backends.el +++ b/aidermacs-backends.el @@ -1,4 +1,10 @@ ;;; aidermacs-backends.el --- Backend implementations for aidermacs.el -*- lexical-binding: t; -*- +;; Author: Mingde (Matthew) Zeng <matthew...@posteo.net> +;; Version: 0.5.0 +;; Package-Requires: ((emacs "26.1") (transient "0.3.0")) +;; Keywords: ai emacs agents llm aider ai-pair-programming, convenience, tools +;; URL: https://github.com/MatthewZMD/aidermacs.el +;; Originally inspired by: Kang Tu <tni...@gmail.com> Aider.el ;;; Commentary: ;; Backend dispatcher for aidermacs.el diff --git a/aidermacs-doom.el b/aidermacs-doom.el index ed1be9a0f6..1b457fbd91 100644 --- a/aidermacs-doom.el +++ b/aidermacs-doom.el @@ -1,4 +1,10 @@ ;;; aidermacs-doom.el --- Description -*- lexical-binding: t; no-byte-compile: t -*- +;; Author: Mingde (Matthew) Zeng <matthew...@posteo.net> +;; Version: 0.5.0 +;; Package-Requires: ((emacs "26.1") (transient "0.3.0")) +;; Keywords: ai emacs agents llm aider ai-pair-programming, convenience, tools +;; URL: https://github.com/MatthewZMD/aidermacs.el +;; Originally inspired by: Kang Tu <tni...@gmail.com> Aider.el ;; ;; This file is not part of GNU Emacs. ;; diff --git a/aidermacs-helm.el b/aidermacs-helm.el index 4dc5bfa2af..d6309fc24f 100644 --- a/aidermacs-helm.el +++ b/aidermacs-helm.el @@ -1,11 +1,10 @@ ;;; aidermacs-helm.el --- Helm completion for aidermacs.el -*- lexical-binding: t; -*- - ;; Author: Mingde (Matthew) Zeng <matthew...@posteo.net> -;; Original Author: Kang Tu <tni...@gmail.com> -;; Version: 0.2.0 -;; Package-Requires: ((emacs "25.1") (helm "3.0")) -;; Keywords: convenience, tools +;; Version: 0.5.0 +;; Package-Requires: ((emacs "26.1") (transient "0.3.0")) +;; Keywords: ai emacs agents llm aider ai-pair-programming, convenience, tools ;; URL: https://github.com/MatthewZMD/aidermacs.el +;; Originally inspired by: Kang Tu <tni...@gmail.com> Aider.el ;;; Commentary: ;; Optional Helm completion interface for aidermacs.el diff --git a/aidermacs-models.el b/aidermacs-models.el index 28a712137f..41c0ee7d57 100644 --- a/aidermacs-models.el +++ b/aidermacs-models.el @@ -1,4 +1,10 @@ ;;; aidermacs-models.el --- Model selection for aidermacs.el -*- lexical-binding: t; -*- +;; Author: Mingde (Matthew) Zeng <matthew...@posteo.net> +;; Version: 0.5.0 +;; Package-Requires: ((emacs "26.1") (transient "0.3.0")) +;; Keywords: ai emacs agents llm aider ai-pair-programming, convenience, tools +;; URL: https://github.com/MatthewZMD/aidermacs.el +;; Originally inspired by: Kang Tu <tni...@gmail.com> Aider.el ;;; Commentary: ;; Model selection functionality for aidermacs.el diff --git a/aidermacs-utils.el b/aidermacs-utils.el index bea25e2f4e..93b6e76d7e 100644 --- a/aidermacs-utils.el +++ b/aidermacs-utils.el @@ -1,3 +1,9 @@ +;; Author: Mingde (Matthew) Zeng <matthew...@posteo.net> +;; Version: 0.5.0 +;; Package-Requires: ((emacs "26.1") (transient "0.3.0")) +;; Keywords: ai emacs agents llm aider ai-pair-programming, convenience, tools +;; URL: https://github.com/MatthewZMD/aidermacs.el +;; Originally inspired by: Kang Tu <tni...@gmail.com> Aider.el ;; New function to run `find-name-dired` from the Git repository root directory ;;;###autoload diff --git a/aidermacs.el b/aidermacs.el index c7d93d2062..dec7a92118 100644 --- a/aidermacs.el +++ b/aidermacs.el @@ -1,11 +1,10 @@ ;;; aidermacs.el --- aidermacs package for interactive conversation with aidermacs -*- lexical-binding: t; -*-a - ;; Author: Mingde (Matthew) Zeng <matthew...@posteo.net> -;; Original Author: Kang Tu <tni...@gmail.com> -;; Version: 0.2.0 +;; Version: 0.5.0 ;; Package-Requires: ((emacs "26.1") (transient "0.3.0")) -;; Keywords: convenience, tools +;; Keywords: ai emacs agents llm aider ai-pair-programming, convenience, tools ;; URL: https://github.com/MatthewZMD/aidermacs.el +;; Originally inspired by: Kang Tu <tni...@gmail.com> Aider.el ;;; Commentary: ;; This package provides an interactive interface to communicate with https://github.com/paul-gauthier/aidermacs. diff --git a/examples/README.org b/examples/README.org deleted file mode 100644 index 532b14264e..0000000000 --- a/examples/README.org +++ /dev/null @@ -1,16 +0,0 @@ - -Two games were written with the assistance of aider / aider.el - -- [[./battleship][battleship]] - https://en.wikipedia.org/wiki/Battleship_(game) - - 2.5h to write the game with human vs. human mode - - using DeepSeek model - - 1.5h to iterate and add human vs. computer mode - - using Claude model - - 1h to have a better AI - - using OpenAI o3-mini model -- [[./connect4][connect4]] - https://en.wikipedia.org/wiki/Connect_Four - - 0.5h to write the game with basic human vs. computer mode - - 0.5h to iterate the AI and make it more effective - - all using Claude model - -- These games are CLI-based text games. In the future, I plan to create graphical versions using pygame, with the help of aider and aider.el diff --git a/examples/battleship/README.org b/examples/battleship/README.org deleted file mode 100644 index b2ae63669c..0000000000 --- a/examples/battleship/README.org +++ /dev/null @@ -1,167 +0,0 @@ - -* Introduce of the project - -Recently, My son play the [[https://en.wikipedia.org/wiki/Battleship_(game)][Battle Ship board game]] with me, this is a game with 90 years history. My son enjoy this. - -- [[https://www.amazon.com/dp/B06Y1N3PTX?ref=ppx_yo2ov_dt_b_fed_asin_title&th=1][Amazon link to Battle Ship Game]], it is still top 20 of board game in Amazon. - -I thought it would be a interesting to implement the game in Python, with help of aider (and aider.el). - -The implementation took totally around 4 hours to have a human player vs a pretty good computer player. - -Most of the code were written by aider + deepseek model (price is 1% of chatgpt, but it is a little bit slow) (~3 hour) - -The Computer Player AI part was written by new published anthropic/claude-3-5-sonnet-20241022 model, and aider support that. (~1 hour) - -* Game Design - -** Data structure - -*** Fleet - - list of (x, y) coordinates - - every x, y must be - - inside the board (0 <= x < 10, 0 <= y < 10) - - unique - - adjacent to each other - - in the same direction - - x, y are generated by the init function below - - name: choice of carrier, battleship, cruiser, submarine, destroyer - - init(x, y, size, direction, name) - - size: 2, 3, 4, 5 - - direction: horizontal or vertical - - hit(x, y) - - return true if the hit is successful - - log the hit point - - is_sunk() - - return true if the fleet is sunk - -*** FleetGroup - - list of Fleet - - init(): create empty fleet group - - add(fleet): add a fleet to the group - - hit(x, y) - - return the fleet name if the hit is successful - - return None if the hit is failed - - all_sunk() - - return true if all fleets are sunk - -*** Player - - fleetGroup - - hitHistory - - list of (x, y, is_hit) - - randomInit(fleetNames=['carrier', 'battleship', 'cruiser', 'submarine', 'destroyer'], boardSize=(10, 10)) - - randomly place n fleets on the board - - n is the size of fleetNames - - fleets cannot overlap each other - - fleet must be inside board - - hit(player, x, y) - - return true if the hit is successful on the player's fleet - - return false if the hit is failed - - log x, y, is_hit to hitHistory - - was_hit(x, y) - - return the fleet name if the hit is successful - - return None if the hit is failed - - all_sunk() - - return true if all fleets are sunk - -**** Human Player - - input_and_hit(player) - - get x, y from the user - - call player.hit(x, y) - -**** Computer Player - - input_and_hit(player) - - smartly choose x, y based on hit history and boat size of the player - - call player.hit(x, y) - -*** Game - - init(fleetNames, boardSize) - - addPlayers(player1, player2) - - run() - - loop until one of the players' all fleets are sunk - - player1 hit player2 - - player2 hit player1 - - print the winner - - two_player_games() - - create player1 and player2 with HumanPlayer and randomInit and addPlayers(player1, player2) - - run() - - - - -* Background: [[https://en.wikipedia.org/wiki/Battleship_(game)][Battleship game (wiki)]] - -- The following information are provided to aider during implementation. - -Battleship (also known as Battleships) is a strategy type guessing game for two players. It is played on ruled grids (paper or board) on which each player's fleet of warships are marked. The locations of the fleets are concealed from the other player. Players alternate turns calling "shots" at the other player's ships, and the objective of the game is to destroy the opposing player's fleet. - -Battleship is known worldwide as a pencil and paper game which dates from World War I. It was published by various companies as a pad-and-pencil game in the 1930s and was released as a plastic board game by Milton Bradley in 1967. The game has spawned electronic versions, video games, smart device apps and a film. -History - -The game of Battleship is thought to have its origins in the French game L'Attaque played during World War I, although parallels have also been drawn to E. I. Horsman's 1890 game Basilinda,[1] and the game is said to have been played by Russian officers before World War I.[3] In 1907 the game playing was mentioned in the diary of Russian poet Ryurik Ivnev.[4] The first commercial version of the game was Salvo, published in 1931 in the United States by the Starex company. Other versions o [...] -A map of one player's ships and the hits against them, from a game in progress. The grey boxes are the ships placed by the player, and the cross marks show the squares that their opponent has fired upon. The player would be tracking the success of their own shots in a separate grid - -In 1967 Milton Bradley introduced a version of the game that used plastic boards and pegs. Conceived by Ed Hutchins, play was on pegboards using miniature plastic ships. In 1977, Milton Bradley also released a computerized Electronic Battleship,[5] a pioneering microprocessor-based toy, capable of generating various sounds.[6] Electronic Battleship was designed by Dennis Wyman and Bing McCoy.[citation needed] It was followed in 1989 by Electronic Talking Battleship.[7] In 2008, an update [...] - -Battleship was one of the earliest games to be produced as a computer game, with a version being released for the Z80 Compucolor in 1979.[1][8] Many computer editions of the game have been produced since. In Clubhouse Games for the Nintendo DS, Battleship is known as Grid Attack.[9] It is played on a 7×7 grid,[10] and includes slight variations, such as four-player gameplay, and various ship sizes and shapes. Versions of Battleship appear as applications on numerous social networking services. - -Battleship was also part of Hasbro Family Game Night for the PlayStation 2 and Wii, as well as the Xbox 360 (Xbox Live Arcade). These alter the rules, including the size of the grid (8×12 in the NES version, 8×8 in the Game Boy version), size of ships (it is common to feature a submarine that takes up a single square) and special shot missiles for each ship. For example, in the NES version, the cruiser has a five-shot missile which strikes five squares in an X pattern on the grid in one [...] - -A minigame version of Battleship was used in the third season of The Hub's Family Game Night, which uses a 5×5 grid and the first team to sink three ships wins the game. - -In 2012, the military science fiction action movie Battleship was released, which was inspired by the Milton Bradley board game, which this adaptation is an alien invasion-theme movie. A version of Battleship based on the movie was released in which one side had alien ship playing pieces. - -In 1973 a spin-off version was released under the name Sub Search. Employing a three-dimensional play area, battleships drop depth charges on submarines hidden on a multi-level board. - -In one episode of the Amazon Prime Video show The Grand Tour, presenters Richard Hammond and James May played a game of Battleship with two cranes (colored red and green) and 20 REVAi vehicles as missiles. The ships ranged from cars to campervans. May won the game after sinking all of Hammond's ships. -Description - -The game is played on four grids, two for each player. The grids are typically square, usually 10×10, and the individual squares in the grid are identified by letter and number.[11] On one grid the player arranges ships and records the shots by the opponent. On the other grid, the player records their own shots. - -Before play begins, each player secretly arranges their ships on their primary grid. Each ship occupies a number of consecutive squares on the grid, arranged either horizontally or vertically. The number of squares for each ship is determined by the type of ship. The ships cannot overlap (i.e., only one ship can occupy any given square in the grid) or be placed diagonally. The types and numbers of ships allowed are the same for each player. These may vary depending on the rules. The ship [...] - -The 1990 Milton Bradley version of the rules specify the following ships:[13] -No. Class of ship Size -1 Carrier 5 -2 Battleship 4 -3 Cruiser 3 -4 Submarine 3 -5 Destroyer 2 - -In 2002, Hasbro renamed the Cruiser to Destroyer, taking three squares, and introduced a new two-square ship called the Patrol Boat.[14] -No. Class of ship Size -1 Carrier 5 -2 Battleship 4 -3 Destroyer 3 -4 Submarine 3 -5 Patrol Boat 2 - -After the ships have been positioned, the game proceeds in a series of rounds. In each round, each player takes a turn to announce a target square in the opponent's grid which is to be shot at. The opponent announces whether or not the square is occupied by a ship. If it is a "hit", the player who is hit marks this on their own "ocean" or grid (with a red peg in the pegboard version), and announces what ship was hit. The attacking player marks the hit or miss on their own "tracking" or " [...] - -When all of the squares of a ship have been hit, the ship's owner announces the sinking of the Carrier, Submarine, Cruiser/Destroyer/Patrol Boat, or the titular Battleship. If all of a player's ships have been sunk, the game is over and their opponent wins. -Variations -Players in a Battleship tournament aboard USS George H.W. Bush - -In the 1931 Salvo edition of the game, players target a specified number of squares at one time, and all of the squares are attacked simultaneously. A player may initially target five (one for each unsunken ship) squares per turn, and the amount of shots decreases when one of the player's ships are lost.[3] In other variants of this mechanic, the number of shots allowed to fire each turn may either be fixed at five for the whole game, be equal to the number of unsunken ships belonging to [...] - -One variant of Battleship allows players to decline to announce that a ship has been sunk, requiring their opponent to take further shots in order to confirm that an area is clear.[1] Another variant of the rule allows a player to move one of their ships to a new, uncalled location every fourth or fifth move.[1] -Reviews - - Family Games: The 100 Best[15] - -See also - - Battleship (film), 2012 film adaptation - Battleship (puzzle) - Video game adaptations: - Battleships, 1987, for ZX Spectrum, Commodore 64, Amstrad CPC, BBC Micro, and Amiga computers - Battleship, 1993, for Nintendo Entertainment System and Game Gear systems - Super Battleship, 1993, for Genesis and Super NES systems - Battleship, 1996, for PCs - Battleship: Surface Thunder, 2000, for PCs - Battleship, a 2012 tie-in to the film above, for PlayStation 3, Xbox 360, Wii, Nintendo DS, and Nintendo 3DS - Similar games: - Minesweeper, logic puzzle video game genre - Mugwump, 1973 computer game - Wargame, strategy game genre - diff --git a/examples/battleship/fleet.py b/examples/battleship/fleet.py deleted file mode 100644 index ff1c044c04..0000000000 --- a/examples/battleship/fleet.py +++ /dev/null @@ -1,61 +0,0 @@ - -class Fleet: - def __init__(self, x, y, size, direction, name): - self.coordinates = [] - self.name = name - self.size = size - self.direction = direction - self.hits = [] - - if direction == 'vertical': - for i in range(size): - self.coordinates.append((x + i, y)) - elif direction == 'horizontal': - for i in range(size): - self.coordinates.append((x, y + i)) - - def hit(self, x, y): - if (x, y) in self.coordinates and (x, y) not in self.hits: - self.hits.append((x, y)) - print(f"The {self.name} has been hitted, total life is {self.size}, rest life is {self.size - len(self.hits)}.") - if self.is_sunk(): - print(f"The {self.name} is destroyed.") - return True - return False - - def is_sunk(self): - return len(self.hits) == self.size - - def show(self): - print(f"Name: {self.name}") - print(f"Size: {self.size}") - print("Coordinates:") - for coord in self.coordinates: - status = "Hit" if coord in self.hits else "Not Hit" - print(f" {coord}: {status}") - - -class FleetGroup: - def __init__(self): - self.fleets = [] - - def add(self, fleet): - self.fleets.append(fleet) - - def hit(self, x, y): - for fleet in self.fleets: - if fleet.hit(x, y): - return fleet.name - return None - - def all_sunk(self): - for fleet in self.fleets: - if not fleet.is_sunk(): - return False - return True - - def show(self): - for fleet in self.fleets: - fleet.show() - print() # add an empty line to separate fleets - diff --git a/examples/battleship/game.py b/examples/battleship/game.py deleted file mode 100644 index d6e3164498..0000000000 --- a/examples/battleship/game.py +++ /dev/null @@ -1,384 +0,0 @@ - -from fleet import Fleet, FleetGroup - - -class Player: - - def __init__(self, name, board_size=(10, 10)): - self.name = name - self.board_size = board_size - self.fleet_group = FleetGroup() - self.hit_history = [] - self.was_hit_history = [] - - def random_init(self, fleet_names=['carrier', 'battleship', 'cruiser', 'submarine', 'destroyer']): - board_size = self.board_size - import random - board = [[0] * board_size[1] for _ in range(board_size[0])] - - def is_valid_placement(x, y, size, direction): - if direction == 'vertical': - return all(board[x + i][y] == 0 for i in range(size)) - else: - return all(board[x][y + i] == 0 for i in range(size)) - - for name in fleet_names: - fleet_sizes = { - 'carrier': 5, - 'battleship': 4, - 'cruiser': 3, - 'submarine': 3, - 'destroyer': 3, - 'patrol boat': 1, - 'boater': 2, - 'bomber': 6, - 'speed boat': 3, - 'smasher': 2, - 'f-1': 2 - } - size = fleet_sizes[name] - direction = random.choice(['vertical', 'horizontal']) - placed = False - - while not placed: - if direction == 'vertical': - x = random.randint(0, board_size[0] - size) - y = random.randint(0, board_size[1] - 1) - else: - x = random.randint(0, board_size[0] - 1) - y = random.randint(0, board_size[1] - size) - - if is_valid_placement(x, y, size, direction): - placed = True - fleet = Fleet(x, y, size, direction, name) - self.fleet_group.add(fleet) - for i in range(size): - if direction == 'vertical': - board[x + i][y] = 1 - else: - board[x][y + i] = 1 - placed = True - - def hit(self, player, x, y): - result = player.fleet_group.hit(x, y) - self.hit_history.append((x, y, result is not None)) - return result - - def was_hit(self, x, y): - self.was_hit_history.append((x, y)) - return self.fleet_group.hit(x, y) - - def all_sunk(self): - return self.fleet_group.all_sunk() - - def show_myself(self): - # show a x*y grid, which x=board_size[0], y=board_size[1] - # if a cell is not being hit, show '.' - # if a cell is occupied by a fleet and didn't got hit, show 'O' - # if a cell is occupied by a fleet and got hit, show 'X' - # if a cell is not occupied by a fleet but was hit before, show '*' - board_size = self.board_size - board = [['.'] * board_size[1] for _ in range(board_size[0])] - for fleet in self.fleet_group.fleets: - for coord in fleet.coordinates: - if coord in fleet.hits: - board[coord[0]][coord[1]] = 'X' - else: - board[coord[0]][coord[1]] = 'O' - for hit in self.was_hit_history: - if not board[hit[0]][hit[1]] == 'O': - board[hit[0]][hit[1]] = '*' - # print the board - # print 1-10 on the top - print(' ' + ' '.join([str(i) for i in range(1, board_size[1] + 1)])) - for i in range(board_size[0]): - row = board[i] - # print A-J on the left - ch = chr(ord('A') + i) - print(ch + ' ' + ' '.join(row)) - - def show_enemy(self): - # draw the board given hit_history, similar to show_myself - board_size = self.board_size - board = [['.'] * board_size[1] for _ in range(board_size[0])] - for hit in self.hit_history: - if hit[2]: - board[hit[0]][hit[1]] = 'X' - else: - board[hit[0]][hit[1]] = '*' - # print the board - # print 1-10 on the top - print(' ' + ' '.join([str(i) for i in range(1, board_size[1] + 1)])) - for i in range(board_size[0]): - row = board[i] - # print A-J on the left - ch = chr(ord('A') + i) - print(ch + ' ' + ' '.join(row)) - - def log_hit(self, x, y, hit_result): - loc = self.encodeLoc(x, y) - print(f"{self.name} shoot on {loc}: ({x}, {y})") - if hit_result: - print(f"{self.name} hit on " + hit_result) - else: - print(f"{self.name} miss") - - def print_board(self): - print(f"{self.name}'s hit history: ") - self.show_enemy() - print("-------------------------------------") - print(f"{self.name}'s board: ") - self.show_myself() - - def encodeLoc(self, x, y): - return chr(x + ord('A')) + str(y + 1) - - -class HumanPlayer(Player): - - def input_and_hit(self, player): - while True: - loc = input(f"{self.name}, enter location to hit (A1 to J10): ") - if loc == 'exit' or loc == 'quit': - print("Game ended") - ## quit the whole program and back to shell - import sys - sys.exit() - else: - try: - x, y = self.decodeLoc(loc) - break - except: - print("Invalid location, please try again") - pass - hit_result = self.hit(player, x, y) - self.log_hit(x, y, hit_result) - self.print_board() - print("=====================================") - return hit_result - - def decodeLoc(self, loc): - x = ord(loc[0].upper()) - ord('A') - y = int(loc[1:]) - 1 - return x, y - - -class ComputerPlayer(Player): - - def input_and_hit(self, player): - import random - board_size = self.board_size - hit_history = self.hit_history - - # get sizes of unsunk ships - remaining_sizes = [] - for fleet in player.fleet_group.fleets: - if not fleet.is_sunk(): - remaining_sizes.append(fleet.size) - - # get all consecutive hit points that are not sunk - hits_in_progress = [] - hits_only = [(x, y) for x, y, hit in hit_history if hit] - - # Analyze the orientation of consecutive hit points - for x, y in hits_only: - # check if this hit is part of a sunk ship - is_sunk = False - for fleet in player.fleet_group.fleets: - if fleet.is_sunk() and (x, y) in fleet.coordinates: - is_sunk = True - break - if not is_sunk: - hits_in_progress.append((x, y)) - - if hits_in_progress: - # If there are multiple consecutive hit points, analyze possible orientation - if len(hits_in_progress) > 1: - # Determine the orientation - points = sorted(hits_in_progress) - if points[0][0] == points[1][0]: # horizontal direction - direction = 'horizontal' - x = points[0][0] - possible_y = [points[0][1]-1, points[-1][1]+1] - candidates = [(x, y) for y in possible_y if 0 <= y < board_size[1]] - else: # vertical direction - direction = 'vertical' - y = points[0][1] - possible_x = [points[0][0]-1, points[-1][0]+1] - candidates = [(x, y) for x in possible_x if 0 <= x < board_size[0]] - else: - # For a single hit point, try all four directions - x, y = hits_in_progress[0] - candidates = [(x-1,y), (x+1,y), (x,y-1), (x,y+1)] - candidates = [(x,y) for x,y in candidates - if 0 <= x < board_size[0] and 0 <= y < board_size[1]] - - # Filter out positions that have already been targeted - candidates = [(x,y) for x,y in candidates - if (x,y,True) not in hit_history and (x,y,False) not in hit_history] - - if candidates: - x, y = random.choice(candidates) - else: - # If no valid adjacent cell is available, choose randomly - x, y = self._random_shot(board_size, hit_history) - else: - # If there are no ongoing hit sequences, use a random shot - x, y = self._random_shot(board_size, hit_history) - - hit_result = self.hit(player, x, y) - self.log_hit(x, y, hit_result) - print(f"{player.name}'s board: ") - player.show_myself() - print("=====================================") - return hit_result - - def _random_shot(self, board_size, hit_history): - import random - # 创建所有可能的坐标 - possible_hits = [(x, y) for x in range(board_size[0]) - for y in range(board_size[1]) - if (x,y,True) not in hit_history and (x,y,False) not in hit_history] - - # 优先选择间隔为2的点位(因为最小的船是2格) - priority_hits = [(x,y) for x,y in possible_hits if (x+y)%2 == 0] - - if priority_hits: - return random.choice(priority_hits) - return random.choice(possible_hits) - - -class HardComputerPlayer(ComputerPlayer): - """ - Hard mode: use probability density strategy for selecting the best move - """ - - def input_and_hit(self, player): - # Get board size and hit history - board_size = self.board_size - hit_history = self.hit_history - - # get lengths of all opponent's unsunk ships - remaining_ships = [fleet.size for fleet in player.fleet_group.fleets if not fleet.is_sunk()] - - # initialize probability grid with zeros - prob_grid = [[0] * board_size[1] for _ in range(board_size[0])] - - # record shot coordinates; elements in hit_history are tuples (x, y, bool) - shot_cells = {(x, y): True for x, y, _ in hit_history} - - # 对于每个剩余舰船的长度,尝试水平和垂直的放置,并累加概率分值 - for size in remaining_ships: - for x in range(board_size[0]): - for y in range(board_size[1]): - # horizontal placement: check if cells from (x, y) to (x, y+size-1) are within boundaries and not shot - if y + size <= board_size[1]: - if all((x, y + offset) not in shot_cells for offset in range(size)): - for offset in range(size): - prob_grid[x][y + offset] += size # Increase weight based on ship size - - # vertical placement: check if cells from (x, y) to (x+size-1, y) are within boundaries and not shot - if x + size <= board_size[0]: - if all((x + offset, y) not in shot_cells for offset in range(size)): - for offset in range(size): - prob_grid[x + offset][y] += size # Increase weight based on ship size - - # Add bonus for cells adjacent to unsunk hit cells - bonus = 3 # Bonus weight for adjacent cell - for x, y, hit in hit_history: - if hit: # cell was a hit - # Check if this hit cell belongs to an unsunk fleet - unsunk = False - for fleet in player.fleet_group.fleets: - if (x, y) in fleet.coordinates and not fleet.is_sunk(): - unsunk = True - break - if unsunk: - for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]: - nx, ny = x + dx, y + dy - if 0 <= nx < board_size[0] and 0 <= ny < board_size[1] and (nx, ny) not in shot_cells: - prob_grid[nx][ny] += bonus # Boost adjacent cells near successful hit - max_prob = -1 - candidates = [] - for x in range(board_size[0]): - for y in range(board_size[1]): - if (x, y) not in shot_cells: - cell_prob = prob_grid[x][y] - if cell_prob > max_prob: - max_prob = cell_prob - candidates = [(x, y)] - elif cell_prob == max_prob: - candidates.append((x, y)) - - import random - if candidates: - x, y = random.choice(candidates) - else: - # 如果没有候选,退回到父类的随机策略 - x, y = self._random_shot(board_size, hit_history) - - hit_result = self.hit(player, x, y) - self.log_hit(x, y, hit_result) - print(f"{player.name}'s board: ") - player.show_myself() - print("=====================================") - return hit_result - -class Game: - - def __init__(self, fleet_names, board_size): - self.fleet_names = fleet_names - self.board_size = board_size - self.players = [] - - def add_players(self, player1, player2): - self.players.append(player1) - print(f"{player1.name} join the game") - self.players.append(player2) - print(f"{player2.name} join the game") - - def run(self): - player1, player2 = self.players - n = 1 - while not player1.all_sunk() and not player2.all_sunk(): - print(f"Round {n}") - hit_result = player1.input_and_hit(player2) - if player2.all_sunk(): - print(f"{player1.name} wins!") - break - player2.input_and_hit(player1) - if player1.all_sunk(): - print(f"{player2.name} wins!") - break - - def two_player_games(self): - player1 = HumanPlayer("You", self.board_size) - player2 = ComputerPlayer("Play Kang", self.board_size) - player1.random_init(self.fleet_names) - player2.random_init(self.fleet_names) - self.add_players(player1, player2) - self.run() - - def computer_vs_hard(self, normal_first=True): - """ - # Start a game with ComputerPlayer vs HardComputerPlayer - """ - player1 = ComputerPlayer("Normal CPU", self.board_size) - player2 = HardComputerPlayer("Hard CPU", self.board_size) - player1.random_init(self.fleet_names) - player2.random_init(self.fleet_names) - if normal_first: - self.add_players(player1, player2) - else: - self.add_players(player2, player1) - self.run() - - -if __name__ == '__main__': - game = Game(['carrier', 'battleship', 'cruiser', 'submarine', 'destroyer' - # ,'patrol boat', 'boater', 'bomber', 'speed boat', 'smasher', 'f-1' - ], - # (10, 10) - (7, 7) - ) - game.two_player_games() diff --git a/examples/battleship/test_fleet.py b/examples/battleship/test_fleet.py deleted file mode 100644 index f27de461c6..0000000000 --- a/examples/battleship/test_fleet.py +++ /dev/null @@ -1,29 +0,0 @@ - -import unittest - -from fleet import FleetGroup, Fleet - - -class TestFleetGroup(unittest.TestCase): - - def test_hit_and_sunk(self): - fleet = Fleet(0, 0, 2, 'horizontal', 'destroyer') - self.assertFalse(fleet.is_sunk()) - fleet.hit(0, 0) - self.assertFalse(fleet.is_sunk()) - fleet.hit(0, 1) - self.assertTrue(fleet.is_sunk()) - - def test_hit_and_sunk_battleship(self): - fleet = Fleet(0, 0, 5, 'horizontal', 'battleship') - self.assertFalse(fleet.is_sunk()) - fleet.hit(0, 0) - self.assertFalse(fleet.is_sunk()) - fleet.hit(0, 1) - self.assertFalse(fleet.is_sunk()) - fleet.hit(0, 2) - self.assertFalse(fleet.is_sunk()) - fleet.hit(0, 3) - self.assertFalse(fleet.is_sunk()) - fleet.hit(0, 4) - self.assertTrue(fleet.is_sunk()) diff --git a/examples/battleship/test_game.py b/examples/battleship/test_game.py deleted file mode 100644 index bca9e1ae87..0000000000 --- a/examples/battleship/test_game.py +++ /dev/null @@ -1,42 +0,0 @@ - -import unittest - -from game import Player, Game, ComputerPlayer, HardComputerPlayer - - -class TestPlayer(unittest.TestCase): - - def test_player_random_init(self): - player = Player("Play Kang") - player.random_init() - player.show_myself() - print() - player.show_enemy() - # player.fleet_group.show() - - def test_computer_vs_hard(self): - """ - # Unit test for battle between ComputerPlayer and HardComputerPlayer - """ - fleet_names = ['carrier', 'battleship', 'cruiser', 'submarine', 'destroyer'] - board_size = (10, 10) - normal_wins = hard_wins = 0 - for i in range(100): - game = Game(fleet_names, board_size) - if i % 2 == 0: - game.computer_vs_hard(normal_first=True) - if game.players[0].all_sunk(): - normal_wins += 1 - if game.players[1].all_sunk(): - hard_wins += 1 - else: - game.computer_vs_hard(normal_first=False) - if game.players[0].all_sunk(): - hard_wins += 1 - if game.players[1].all_sunk(): - normal_wins += 1 - print(f"Normal wins: {normal_wins}") - print(f"Hard wins: {hard_wins}") - -# Normal wins: 31 -# Hard wins: 69 diff --git a/examples/connect4/README.org b/examples/connect4/README.org deleted file mode 100644 index c4be401029..0000000000 --- a/examples/connect4/README.org +++ /dev/null @@ -1,62 +0,0 @@ - -* [[https://en.wikipedia.org/wiki/Connect_Four][Connect Four]] - -Connect Four (also known as Connect 4, Four Up, Plot Four, Find Four, Captain's Mistress, Four in a Row, Drop Four, and Gravitrips in the Soviet Union) is a game in which the players choose a color and then take turns dropping colored tokens into a six-row, seven-column vertically suspended grid. The pieces fall straight down, occupying the lowest available space within the column. The objective of the game is to be the first to form a horizontal, vertical, or diagonal line of four of on [...] - -The game was created by Howard Wexler, and first sold under the Connect Four trademark[10] by Milton Bradley in February 1974. - -** Gameplay - - Object: Connect four of your discs in a row while preventing your opponent from doing the same. But, look out – your opponent can sneak up on you and win the game! - — Milton Bradley, Connect Four "Pretty Sneaky, Sis" television commercial, 1977[11] - -A gameplay example (right), shows the first player starting Connect Four by dropping one of their yellow discs into the center column of an empty game board. The two players then alternate turns dropping one of their discs at a time into an unfilled column, until the second player, with red discs, achieves a diagonal four in a row, and wins the game. If the board fills up before either player achieves four in a row, then the game is a draw. - -** Mathematical solution - -Connect Four is a two-player game with perfect information for both sides, meaning that nothing is hidden from anyone. Connect Four also belongs to the classification of an adversarial, zero-sum game, since a player's advantage is an opponent's disadvantage. - -One measure of complexity of the Connect Four game is the number of possible games board positions. For classic Connect Four played on a 7-column-wide, 6-row-high grid, there are 4,531,985,219,092 (about 4½ trillion) positions[12] for all game boards populated with 0 to 42 pieces. - -The game was first solved by James Dow Allen (October 1, 1988), and independently by Victor Allis (October 16, 1988).[13] Allis describes a knowledge-based approach,[14] with nine strategies, as a solution for Connect Four. Allen also describes winning strategies[15][16] in his analysis of the game. At the time of the initial solutions for Connect Four, brute-force analysis was not deemed feasible given the game's complexity and the computer technology available at the time. - -Connect Four has since been solved with brute-force methods, beginning with John Tromp's work in compiling an 8-ply database.[13][17] The artificial intelligence algorithms able to strongly solve Connect Four are minimax or negamax, with optimizations that include alpha-beta pruning, move ordering, and transposition tables. The code for solving Connect Four with these methods is also the basis for the Fhourstones[18] integer performance benchmark. - -The solved conclusion for Connect Four is first-player-win. With perfect play, the first player can force a win,[13][14][15] on or before the 41st move[19] by starting in the middle column. The game is a theoretical draw when the first player starts in the columns adjacent to the center. For the edges of the game board, column 1 and 2 on left (or column 7 and 6 on right), the exact move-value score for first player start is loss on the 40th move,[19] and loss on the 42nd move,[19] respec [...] - -* Design - -** Board - -- Data structure - - The board is represented as a 2D array of integers, default is 6 rows * 7 columns. The value of each cell is 0 if it is empty, 1 if it is occupied by a player 1 token, and 2 if it is occupied by a player 2 token. - - The board is initialized with all cells set to 0. - -- Display - - The board is displayed as a grid of 6 rows and 7 columns, with each cell represented by a character. The characters used to represent the empty cell, player 1 token, and player 2 token are '.', 'X', and 'O', respectively. - -** Player - -- The player is represented as an integer, with 1 for player 1 and 2 for player 2. - -- Player can choose a column to drop a token into. The column number is an integer from 1 to 7. - -- If the chosen column is full, the player is prompted to choose another column. - -- When there are four tokens of the same player in a row (horizontally, vertically, or diagonally), the player wins the game. - -*** Human Player - -- The human player is prompted to input the column number to drop a token into. - -*** Computer Player - -- The computer player uses a simple strategy to choose the column to drop a token into. The strategy is to check if there is a winning move available, and if not, to choose a random column. - -** Game - -- The game loop alternates between the two players, allowing each player to take a turn. - -- The game checks for a win condition after each player's turn. If a player wins, the game ends and the winning player is announced. - -- If the board is full and no player has won, the game ends in a draw. diff --git a/examples/connect4/game.py b/examples/connect4/game.py deleted file mode 100644 index cdea2b85c0..0000000000 --- a/examples/connect4/game.py +++ /dev/null @@ -1,263 +0,0 @@ -import random -from typing import List, Optional, Tuple - -class Board: - """Connect Four game board representation""" - - def __init__(self, rows: int = 6, cols: int = 7): - self.rows = rows - self.cols = cols - self.board = [[0 for _ in range(cols)] for _ in range(rows)] - - def display(self): - """Display the current board state""" - symbols = {0: '.', 1: 'X', 2: 'O'} - for row in self.board: - print(' '.join(symbols[cell] for cell in row)) - print(' '.join(str(i+1) for i in range(self.cols))) - print('===========================================') - - def is_valid_move(self, col: int) -> bool: - """Check if a move is valid""" - return 0 <= col < self.cols and self.board[0][col] == 0 - - def drop_token(self, col: int, player: int) -> Optional[Tuple[int, int]]: - """Drop a token in the specified column""" - if not self.is_valid_move(col): - return None - - # Find the lowest empty position - for row in range(self.rows-1, -1, -1): - if self.board[row][col] == 0: - self.board[row][col] = player - return (row, col) - return None - - def check_win(self, row: int, col: int, player: int) -> bool: - """Check if the last move at (row, col) created a winning line""" - directions = [(0,1), (1,0), (1,1), (1,-1)] # horizontal, vertical, diagonal - - for dr, dc in directions: - count = 1 - # Check forward direction - r, c = row + dr, col + dc - while 0 <= r < self.rows and 0 <= c < self.cols and self.board[r][c] == player: - count += 1 - r, c = r + dr, c + dc - - # Check backward direction - r, c = row - dr, col - dc - while 0 <= r < self.rows and 0 <= c < self.cols and self.board[r][c] == player: - count += 1 - r, c = r - dr, c - dc - - if count >= 4: - return True - return False - - def is_full(self) -> bool: - """Check if the board is full""" - return all(cell != 0 for row in self.board for cell in row) - -class Player: - """Base class for players""" - def __init__(self, number: int, name: str): - self.number = number - self.name = name - - def get_move(self, board: Board) -> int: - raise NotImplementedError - -class HumanPlayer(Player): - """Human player implementation""" - def get_move(self, board: Board) -> int: - while True: - try: - col = int(input(f"{self.name}, choose column (1-{board.cols}): ")) - 1 - if board.is_valid_move(col): - return col - print("Invalid move, try again.") - except ValueError: - print("Please enter a valid number.") - -class ComputerPlayer(Player): - - def get_move(self, board: Board) -> int: - valid_cols = [col for col in range(board.cols) if board.is_valid_move(col)] - opponent = 1 if self.number == 2 else 2 - - # 评估每个可能的移动 - moves = [] # 存储(列号,分数)对 - - for col in valid_cols: - board_copy = self._copy_board(board) - row = self._simulate_move(board_copy, col, self.number) - if row == -1: - continue - - score = self._evaluate_move(board_copy, row, col, self.number) - - # 如果这步棋能赢,直接返回 - if board_copy.check_win(row, col, self.number): - return col - - # 检查对手在这个位置是否能赢 - board_copy2 = self._copy_board(board) - row2 = self._simulate_move(board_copy2, col, opponent) - if row2 != -1 and board_copy2.check_win(row2, col, opponent): - score += 100 # 高优先级阻止对手获胜 - - # 检查这步棋是否会给对手创造获胜机会 - if row > 0: # 如果上面还有空间 - board_copy.board[row-1][col] = opponent - if board_copy.check_win(row-1, col, opponent): - score -= 80 # 避免给对手创造获胜机会 - board_copy.board[row-1][col] = 0 - - # 优先选择中间列 - if col == board.cols // 2: - score += 3 - elif col in [board.cols//2-1, board.cols//2+1]: - score += 2 - - # 添加一些随机性 - score += random.uniform(-1, 1) - - moves.append((col, score)) - - # 选择最高分的移动 - if not moves: - return valid_cols[0] - - # 找出得分最高的移动 - max_score = max(score for _, score in moves) - best_moves = [col for col, score in moves if score >= max_score - 2] # 允许差距2分以内的移动 - - return random.choice(best_moves) - - def _copy_board(self, board: Board) -> Board: - """创建棋盘的深拷贝""" - new_board = Board(board.rows, board.cols) - for i in range(board.rows): - for j in range(board.cols): - new_board.board[i][j] = board.board[i][j] - return new_board - - def _simulate_move(self, board: Board, col: int, player: int) -> int: - """模拟在指定列放置棋子,返回行号""" - for row in range(board.rows-1, -1, -1): - if board.board[row][col] == 0: - board.board[row][col] = player - return row - return -1 - - def _evaluate_move(self, board: Board, row: int, col: int, player: int) -> float: - """评估一个位置的分数""" - score = 0 - opponent = 1 if player == 2 else 2 - directions = [(0,1), (1,0), (1,1), (1,-1)] - - # 评估所有方向 - for dr, dc in directions: - # 计算我方连续子数 - my_count = 1 - space_after = 0 - space_before = 0 - - # 正向检查 - r, c = row + dr, col + dc - while 0 <= r < board.rows and 0 <= c < board.cols: - if board.board[r][c] == player: - my_count += 1 - elif board.board[r][c] == 0: - space_after += 1 - break - else: - break - r, c = r + dr, c + dc - - # 反向检查 - r, c = row - dr, col - dc - while 0 <= r < board.rows and 0 <= c < board.cols: - if board.board[r][c] == player: - my_count += 1 - elif board.board[r][c] == 0: - space_before += 1 - break - else: - break - r, c = r - dr, c - dc - - # 根据连续子数和空间评分 - if my_count >= 4: - score += 1000 - elif my_count == 3 and (space_before > 0 or space_after > 0): - score += 50 - elif my_count == 2 and space_before > 0 and space_after > 0: - score += 10 - - # 检查对手在这个方向的威胁 - opp_count = 1 - r, c = row + dr, col + dc - while 0 <= r < board.rows and 0 <= c < board.cols and board.board[r][c] == opponent: - opp_count += 1 - r, c = r + dr, c + dc - - r, c = row - dr, col - dc - while 0 <= r < board.rows and 0 <= c < board.cols and board.board[r][c] == opponent: - opp_count += 1 - r, c = r - dr, c - dc - - if opp_count >= 3: - score += 60 # 高优先级阻止对手 - - return score - -class Game: - """Connect Four game controller""" - def __init__(self, player1, player2): - self.board = Board() - self.players = [player1, player2] - self.current_player = 0 - - def play(self): - """Main game loop""" - n = 0 - while True: - if self.current_player == 0: - n += 1 - print('round ' + str(n)) - self.board.display() - player = self.players[self.current_player] - - # Get player's move - col = player.get_move(self.board) - print(f"{player.name} drop at column {col+1}") - result = self.board.drop_token(col, player.number) - - if result is None: - print("Invalid move!") - continue - - row, col = result - - # Check for win - if self.board.check_win(row, col, player.number): - self.board.display() - print(f"{player.name} wins!") - break - - # Check for draw - if self.board.is_full(): - self.board.display() - print("Game is a draw!") - break - - # Switch players - self.current_player = 1 - self.current_player - -if __name__ == "__main__": - player1 = HumanPlayer(1, "You") - player2 = ComputerPlayer(2, "Play Kang") - game = Game(player1, player2) - game.play() diff --git a/examples/connect4/test_game.py b/examples/connect4/test_game.py deleted file mode 100644 index e85085bee5..0000000000 --- a/examples/connect4/test_game.py +++ /dev/null @@ -1,146 +0,0 @@ -import unittest -from unittest.mock import patch -from io import StringIO -from game import Board, Player, HumanPlayer, ComputerPlayer, Game - -class TestBoard(unittest.TestCase): - def setUp(self): - self.board = Board() - - def test_init(self): - """Test board initialization""" - self.assertEqual(self.board.rows, 6) - self.assertEqual(self.board.cols, 7) - self.assertEqual(len(self.board.board), 6) - self.assertEqual(len(self.board.board[0]), 7) - self.assertTrue(all(cell == 0 for row in self.board.board for cell in row)) - - def test_is_valid_move(self): - """Test valid move checking""" - # Valid moves - self.assertTrue(self.board.is_valid_move(0)) - self.assertTrue(self.board.is_valid_move(6)) - - # Invalid moves - self.assertFalse(self.board.is_valid_move(-1)) - self.assertFalse(self.board.is_valid_move(7)) - - # Fill a column and test - for _ in range(6): - self.board.drop_token(0, 1) - self.assertFalse(self.board.is_valid_move(0)) - - def test_drop_token(self): - """Test token dropping""" - # Drop in empty column - result = self.board.drop_token(0, 1) - self.assertEqual(result, (5, 0)) - self.assertEqual(self.board.board[5][0], 1) - - # Drop on top of another token - result = self.board.drop_token(0, 2) - self.assertEqual(result, (4, 0)) - self.assertEqual(self.board.board[4][0], 2) - - # Drop in full column - for _ in range(4): - self.board.drop_token(0, 1) - result = self.board.drop_token(0, 1) - self.assertIsNone(result) - - def test_check_win(self): - """Test win condition checking""" - # Horizontal win - for i in range(4): - self.board.drop_token(i, 1) - self.assertTrue(self.board.check_win(5, 3, 1)) - - # Vertical win - self.board = Board() # Reset board - for i in range(4): - self.board.drop_token(0, 1) - self.assertTrue(self.board.check_win(2, 0, 1)) - - # Diagonal win - self.board = Board() # Reset board - for i in range(4): - for j in range(i): - self.board.drop_token(i, 2) - self.board.drop_token(i, 1) - self.assertTrue(self.board.check_win(2, 2, 1)) - - def test_is_full(self): - """Test board full condition""" - self.assertFalse(self.board.is_full()) - - # Fill the board - for col in range(7): - for _ in range(6): - self.board.drop_token(col, 1) - self.assertTrue(self.board.is_full()) - -class TestHumanPlayer(unittest.TestCase): - def setUp(self): - self.board = Board() - self.player = HumanPlayer(1) - - @patch('builtins.input', side_effect=['4']) - def test_get_move_valid(self, mock_input): - """Test valid human move input""" - move = self.player.get_move(self.board) - self.assertEqual(move, 3) # Convert from 1-based to 0-based - - @patch('builtins.input', side_effect=['8', '0', 'a', '4']) - def test_get_move_invalid(self, mock_input): - """Test invalid human move inputs""" - move = self.player.get_move(self.board) - self.assertEqual(move, 3) - -class TestComputerPlayer(unittest.TestCase): - def setUp(self): - self.board = Board() - self.player = ComputerPlayer(2) - - def test_get_move(self): - """Test computer move generation""" - move = self.player.get_move(self.board) - self.assertTrue(0 <= move < self.board.cols) - self.assertTrue(self.board.is_valid_move(move)) - - def test_winning_move(self): - """Test computer finds winning move""" - # Set up a winning position - for i in range(3): - self.board.drop_token(i, 2) - move = self.player.get_move(self.board) - self.assertEqual(move, 3) - - def test_blocking_move(self): - """Test computer blocks opponent's winning move""" - # Set up opponent's winning position - for i in range(3): - self.board.drop_token(i, 1) - move = self.player.get_move(self.board) - self.assertEqual(move, 3) - -class TestGame(unittest.TestCase): - def setUp(self): - self.player1 = ComputerPlayer(1) - self.player2 = ComputerPlayer(2) - self.game = Game(self.player1, self.player2) - - def test_init(self): - """Test game initialization""" - self.assertIsInstance(self.game.board, Board) - self.assertEqual(len(self.game.players), 2) - self.assertEqual(self.game.current_player, 0) - - @patch('sys.stdout', new_callable=StringIO) - def test_play_game(self, mock_stdout): - """Test game play until win or draw""" - self.game.play() - output = mock_stdout.getvalue() - self.assertTrue("wins!" in output or "draw!" in output) - -if __name__ == '__main__': - unittest.main() diff --git a/scratch.aider b/scratch.aider deleted file mode 100644 index 7d530e229e..0000000000 --- a/scratch.aider +++ /dev/null @@ -1,24 +0,0 @@ - -/ask what this repo is about? - -# This repository appears to be focused on an Emacs package called "Aidermacs," which -# provides an interactive interface for communicating with an AI tool (likely -# related to programming assistance). The package allows users to send commands -# and queries to the Aider program, which seems to facilitate AI pair programming -# or code assistance. - -# Key features of the repository include: - -# • Interactive Commands: Users can run commands like adding files, asking -# questions, and performing code changes through a transient menu. -# • Integration with Git: The package can generate buffer names based on the -# current Git repository, indicating that it is designed to work within a -# version-controlled environment. -# • Custom Commands: Users can send custom commands to the Aidermacs buffer, allowing -# for flexible interaction with the AI. -# • File Type Association: The package defines a major mode (aidermacs-mode) for -# files with the .aider extension, which includes specific key bindings and -# functionalities tailored for those files. - -# Overall, the repository is aimed at enhancing the coding experience by -# leveraging AI assistance within the Emacs text editor.