initial commit
This commit is contained in:
253
build/chess
Executable file
253
build/chess
Executable file
@@ -0,0 +1,253 @@
|
||||
#!/usr/bin/env python3
|
||||
import curses
|
||||
import chess
|
||||
import chess.engine
|
||||
import os
|
||||
import random
|
||||
|
||||
# Initialize Stockfish engine
|
||||
stockfish_path = "/usr/games/stockfish"
|
||||
engine = chess.engine.SimpleEngine.popen_uci(stockfish_path)
|
||||
|
||||
# Define ASCII/Nerd Font pieces
|
||||
PIECE_SYMBOLS = {
|
||||
'P': '♙', 'N': '♘', 'B': '♗', 'R': '♖', 'Q': '♕', 'K': '♔',
|
||||
'p': '♟', 'n': '♞', 'b': '♝', 'r': '♜', 'q': '♛', 'k': '♚'
|
||||
}
|
||||
|
||||
def draw_board(stdscr, board, cell_width, cell_height, start_y, start_x, selected_square=None, move_history=[], legal_moves=[], player_color=None, difficulty=None):
|
||||
stdscr.clear()
|
||||
|
||||
max_y, max_x = stdscr.getmaxyx()
|
||||
|
||||
# Display the difficulty level
|
||||
stdscr.addstr(0, 0, f"Difficulty Level: {difficulty}")
|
||||
|
||||
# Draw the move history
|
||||
move_history_y = 2
|
||||
stdscr.addstr(1, 0, "Move History:")
|
||||
for index, move in enumerate(move_history[-(max_y // 2 - 2):]): # Display limited history
|
||||
stdscr.addstr(move_history_y + index + 1, 0, f"{index + 1}. {move}")
|
||||
|
||||
# Draw the A-H scale at the top, ensuring each letter corresponds to one block
|
||||
for i, letter in enumerate("ABCDEFGH"):
|
||||
stdscr.addstr(start_y - 1, start_x + i * cell_width + cell_width // 2, letter)
|
||||
|
||||
# Draw the chessboard oriented towards the player
|
||||
for board_y in range(8):
|
||||
actual_row = board_y if player_color == chess.BLACK else 7 - board_y # Orient the row correctly based on player color
|
||||
row_label = str(8 - board_y) if player_color == chess.WHITE else str(board_y + 1)
|
||||
stdscr.addstr(start_y + board_y * cell_height + cell_height // 2, start_x - 2, row_label) # Add row numbers
|
||||
|
||||
for board_x in range(8):
|
||||
actual_col = board_x if player_color == chess.WHITE else 7 - board_x # Orient the column correctly
|
||||
|
||||
piece = board.piece_at(chess.square(actual_col, actual_row))
|
||||
display_piece = PIECE_SYMBOLS[piece.symbol()] if piece else ' '
|
||||
|
||||
# Determine square color
|
||||
square_color = curses.color_pair(1) if (actual_row + actual_col) % 2 == 0 else curses.color_pair(2)
|
||||
is_selected = (selected_square == chess.square(actual_col, actual_row))
|
||||
is_legal_move = chess.square(actual_col, actual_row) in legal_moves
|
||||
|
||||
if is_selected:
|
||||
square_color = curses.color_pair(3) # Highlight selected square
|
||||
elif is_legal_move:
|
||||
square_color = curses.color_pair(4) # Highlight legal move square
|
||||
|
||||
stdscr.attron(square_color)
|
||||
|
||||
# Ensure that the piece is drawn centered in the cell
|
||||
for line in range(cell_height):
|
||||
if line == cell_height // 2: # Only draw the piece on the middle line
|
||||
cell_content = display_piece.center(cell_width)
|
||||
else:
|
||||
cell_content = ' ' * cell_width # Clear remaining lines in the cell
|
||||
|
||||
try:
|
||||
stdscr.addstr(start_y + board_y * cell_height + line, start_x + board_x * cell_width, cell_content)
|
||||
except curses.error:
|
||||
pass
|
||||
|
||||
stdscr.attroff(square_color)
|
||||
|
||||
# Draw column labels again at the bottom to match the board alignment
|
||||
for i, letter in enumerate("ABCDEFGH"):
|
||||
stdscr.addstr(start_y + 8 * cell_height, start_x + i * cell_width + cell_width // 2, letter)
|
||||
|
||||
stdscr.refresh()
|
||||
|
||||
def get_square_from_position(mouse_x, mouse_y, cell_width, cell_height, start_x, start_y, player_color):
|
||||
row = (mouse_y - start_y) // cell_height
|
||||
col = (mouse_x - start_x) // cell_width
|
||||
if player_color == chess.BLACK:
|
||||
col = 7 - col # Reverse column for black orientation
|
||||
if player_color == chess.WHITE:
|
||||
row = 7 - row # Reverse row for white orientation
|
||||
if 0 <= row < 8 and 0 <= col < 8:
|
||||
return chess.square(col, row)
|
||||
return None
|
||||
|
||||
def handle_game_over(stdscr, board, player_color):
|
||||
outcome = board.outcome()
|
||||
if outcome.winner is not None:
|
||||
if outcome.winner == player_color:
|
||||
stdscr.addstr(20, 0, "Checkmate! You win!")
|
||||
else:
|
||||
stdscr.addstr(20, 0, "Checkmate! You lose!")
|
||||
elif outcome.termination == chess.Termination.STALEMATE:
|
||||
stdscr.addstr(20, 0, "Stalemate! It's a draw.")
|
||||
elif outcome.termination == chess.Termination.INSUFFICIENT_MATERIAL:
|
||||
stdscr.addstr(20, 0, "Insufficient material. It's a draw.")
|
||||
elif outcome.termination == chess.Termination.FIFTY_MOVE_RULE:
|
||||
stdscr.addstr(20, 0, "Draw by fifty-move rule.")
|
||||
elif outcome.termination == chess.Termination.THREEFOLD_REPETITION:
|
||||
stdscr.addstr(20, 0, "Draw by threefold repetition.")
|
||||
else:
|
||||
stdscr.addstr(20, 0, "The game is over.")
|
||||
|
||||
stdscr.addstr(21, 0, "Press 'q' to quit.")
|
||||
stdscr.refresh()
|
||||
while stdscr.getch() != ord('q'):
|
||||
pass
|
||||
|
||||
def main(stdscr):
|
||||
curses.curs_set(0)
|
||||
stdscr.clear()
|
||||
curses.mousemask(curses.ALL_MOUSE_EVENTS | curses.REPORT_MOUSE_POSITION)
|
||||
curses.start_color()
|
||||
curses.init_pair(1, curses.COLOR_WHITE, 94) # Light brown background
|
||||
curses.init_pair(2, curses.COLOR_BLACK, 58) # Dark brown background
|
||||
curses.init_pair(3, curses.COLOR_BLACK, curses.COLOR_WHITE) # Highlight selected square in grey
|
||||
curses.init_pair(4, curses.COLOR_GREEN, curses.COLOR_WHITE) # Highlight legal move square
|
||||
|
||||
# Get difficulty input
|
||||
stdscr.addstr(0, 0, "Enter difficulty level (1-20): ")
|
||||
stdscr.refresh()
|
||||
while True:
|
||||
try:
|
||||
difficulty_input = stdscr.getstr().decode().strip()
|
||||
difficulty = int(difficulty_input)
|
||||
if 1 <= difficulty <= 20:
|
||||
break
|
||||
else:
|
||||
stdscr.addstr(1, 0, "Invalid input. Please enter a number between 1 and 20.")
|
||||
except ValueError:
|
||||
stdscr.addstr(1, 0, "Invalid input. Please enter a valid integer.")
|
||||
|
||||
stdscr.refresh()
|
||||
stdscr.getch()
|
||||
stdscr.clear()
|
||||
stdscr.addstr(0, 0, "Enter difficulty level (1-20): ")
|
||||
|
||||
# Randomly assign player color
|
||||
player_color = chess.WHITE if random.choice([True, False]) else chess.BLACK
|
||||
player_side = "White" if player_color == chess.WHITE else "Black"
|
||||
stdscr.clear()
|
||||
stdscr.addstr(0, 0, f"You are playing as: {player_side}")
|
||||
stdscr.refresh()
|
||||
stdscr.getch()
|
||||
|
||||
board = chess.Board()
|
||||
move_history = []
|
||||
undo_stack = []
|
||||
redo_stack = []
|
||||
cell_width = 5 # Adjusted size for larger icons
|
||||
cell_height = 3
|
||||
selected_square = None
|
||||
piece_selected = False
|
||||
moving_mode = False
|
||||
legal_moves = []
|
||||
original_square = None
|
||||
|
||||
while True:
|
||||
rows, cols = stdscr.getmaxyx()
|
||||
cell_width = max(5, min(6, (cols - 20) // 16)) # Ensure room for the move history
|
||||
cell_height = 3
|
||||
start_y = (rows - cell_height * 8) // 2
|
||||
start_x = 20 # Start drawing the board from column 20 to make space for move history
|
||||
|
||||
draw_board(stdscr, board, cell_width, cell_height, start_y, start_x, selected_square, move_history, legal_moves, player_color, difficulty)
|
||||
|
||||
if board.is_game_over():
|
||||
handle_game_over(stdscr, board, player_color)
|
||||
break
|
||||
|
||||
player_turn = board.turn == player_color
|
||||
|
||||
while player_turn:
|
||||
try:
|
||||
key = stdscr.getch()
|
||||
|
||||
if key == ord('q'):
|
||||
engine.quit()
|
||||
return
|
||||
|
||||
if key == ord('u'): # Undo both your move and the computer's move
|
||||
if len(board.move_stack) >= 2:
|
||||
move1 = board.pop()
|
||||
move2 = board.pop()
|
||||
undo_stack.append((move1, move2))
|
||||
redo_stack.clear() # Clear redo stack as we made a new undo
|
||||
move_history.pop()
|
||||
move_history.pop()
|
||||
draw_board(stdscr, board, cell_width, cell_height, start_y, start_x, selected_square, move_history, legal_moves, player_color, difficulty)
|
||||
break
|
||||
else:
|
||||
stdscr.addstr(rows - 1, 0, "No more moves to undo.")
|
||||
stdscr.refresh()
|
||||
|
||||
if key == ord('r'): # Redo both your move and the computer's move
|
||||
if len(undo_stack) > 0:
|
||||
move1, move2 = undo_stack.pop()
|
||||
board.push(move2) # Push computer's move first
|
||||
board.push(move1) # Push your move
|
||||
redo_stack.append((move1, move2))
|
||||
move_history.append(f"{'White' if player_color == chess.WHITE else 'Black'}: {move1}")
|
||||
move_history.append(f"{'Black' if player_color == chess.WHITE else 'White'}: {move2}")
|
||||
draw_board(stdscr, board, cell_width, cell_height, start_y, start_x, selected_square, move_history, legal_moves, player_color, difficulty)
|
||||
break
|
||||
else:
|
||||
stdscr.addstr(rows - 1, 0, "No more moves to redo.")
|
||||
stdscr.refresh()
|
||||
|
||||
if key == curses.KEY_MOUSE:
|
||||
_, mouse_x, mouse_y, _, button_state = curses.getmouse()
|
||||
|
||||
if button_state & curses.BUTTON1_CLICKED:
|
||||
square = get_square_from_position(mouse_x, mouse_y, cell_width, cell_height, start_x, start_y, player_color)
|
||||
if square is not None:
|
||||
if not piece_selected and board.piece_at(square) and board.piece_at(square).color == player_color:
|
||||
selected_square = square
|
||||
original_square = selected_square
|
||||
piece_selected = True
|
||||
moving_mode = False
|
||||
legal_moves = [move.to_square for move in board.legal_moves if move.from_square == selected_square]
|
||||
draw_board(stdscr, board, cell_width, cell_height, start_y, start_x, selected_square, move_history, legal_moves, player_color, difficulty)
|
||||
elif piece_selected and selected_square != square:
|
||||
move = chess.Move(selected_square, square)
|
||||
if move in board.legal_moves:
|
||||
board.push(move)
|
||||
move_history.append(f"{player_side}: {move}")
|
||||
selected_square = None
|
||||
piece_selected = False
|
||||
redo_stack.clear() # Clear redo stack when a new move is made
|
||||
player_turn = False
|
||||
break
|
||||
selected_square = None
|
||||
piece_selected = False
|
||||
|
||||
except curses.error:
|
||||
pass
|
||||
|
||||
if not player_turn:
|
||||
result = engine.play(board, chess.engine.Limit(time=1.0 + (difficulty - 1) * 0.1))
|
||||
board.push(result.move)
|
||||
move_history.append(f"{'White' if not player_color else 'Black'}: {result.move}")
|
||||
undo_stack.clear() # Clear undo stack after computer move
|
||||
|
||||
engine.quit()
|
||||
|
||||
if __name__ == "__main__":
|
||||
curses.wrapper(main)
|
||||
|
||||
34
build/install.sh
Normal file
34
build/install.sh
Normal file
@@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Check Dependencies
|
||||
check_dependency() {
|
||||
if ! command -v "$1" &> /dev/null; then
|
||||
read -p "$1 us not installed. Install? [Y/n]"
|
||||
choice=${choice:-Y}
|
||||
if [[ "$choice" =~ ^[Yy]$ ]]; then
|
||||
echo "Installing $1"
|
||||
sudo apt-get install -y "$1"
|
||||
else
|
||||
echo "Stopping..."
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "$1 is already installed"
|
||||
fi
|
||||
}
|
||||
|
||||
WORKING_DIR=$(pwd)
|
||||
VENV_DIR="/lib/python-venvs/chess"
|
||||
BINARY_DIR="/usr/local/bin/chess"
|
||||
|
||||
if [ ! -d "/lib:/python-venvs" ]; then
|
||||
sudo mkdir -p /lib/python-venvs
|
||||
sudo chmod 755 /lib/python-venvs
|
||||
fi
|
||||
|
||||
if [ ! -d "$VENV_DIR" ]; then
|
||||
echo "Creating system-wide virtual environemnt..."
|
||||
sudo python3 -m venv "$VENV_DIR"
|
||||
fi
|
||||
|
||||
|
||||
Reference in New Issue
Block a user