0% found this document useful (0 votes)
6 views17 pages

Chess

The document is a script for a chess game that includes sound management, touch controls, and save/load functionality for game state. It initializes the chess board, manages game rules, and handles user interactions. The script also includes functions to save and load the game state in JSON format, tracking the board layout, game conditions, and player settings.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
6 views17 pages

Chess

The document is a script for a chess game that includes sound management, touch controls, and save/load functionality for game state. It initializes the chess board, manages game rules, and handles user interactions. The script also includes functions to save and load the game state in JSON format, tracking the board layout, game conditions, and player settings.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
You are on page 1/ 17

#include "speech.

nvgt"
#include "menu.nvgt"
#include "chess_check.nvgt"
#include "chess_ai.nvgt"

// --- ADDED: Chess-specific sound management variables ---


pack_file@ gChessSoundPack = null;
sound@ gChessSoundHandle = sound();

// --- ADDED: A dedicated sound function for the chess game ---
void playChessSound(const string &in filename) {
pack_file@ pkToUse = @gChessSoundPack;
if (@pkToUse is null && @gMainMenuPack !is null && gMainMenuPack.active) {
@pkToUse = @gMainMenuPack;
}
if (@pkToUse is null || filename == "" || @gChessSoundHandle is null) return;
if (gChessSoundHandle.playing) { gChessSoundHandle.stop(); }
if (gChessSoundHandle.load(filename, pkToUse)) {
gChessSoundHandle.play();
}
}

// --- NEW: Chess-specific touch control activation ---


void activateChessTouchControls() {
gActiveInterfaces.resize(0);
dictionary@ chess_gesture_map = {
{"swipe_up1f", KEY_UP},
{"swipe_down1f", KEY_DOWN},
{"swipe_left1f", KEY_LEFT},
{"swipe_right1f", KEY_RIGHT},
{"double_tap1f", KEY_RETURN},
{"swipe_left2f", KEY_ESCAPE},
{"swipe_up2f", KEY_F1},
{"swipe_down2f", KEY_F2} // F2 for saving
};
if (chess_gesture_map.get_size() > 0) {
touch_keyboard_interface@ new_interface =
touch_keyboard_interface(gTouchManager, chess_gesture_map);
gActiveInterfaces.insert_last(new_interface);
}
gTouchManager.set_touch_interfaces(gActiveInterfaces);
}

// Chess board representation


const int BOARD_SIZE = 8;
array<array<string>> board;

// Current cursor position


int cursor_x = 0;
int cursor_y = 0;
bool piece_selected = false;
int selected_x = -1;
int selected_y = -1;
bool player_is_white = true;
bool vs_computer = false;
bool is_human_white = true;

// Draw condition and game state variables


int fifty_move_counter = 0;
dictionary@ position_history;
bool game_over = false;

// Track castling and en passant rights


bool white_king_moved = false;
bool black_king_moved = false;
bool white_rook_a_moved = false;
bool white_rook_h_moved = false;
bool black_rook_a_moved = false;
bool black_rook_h_moved = false;
int en_passant_x = -1;
int en_passant_y = -1;

// Initialize the chess board


void init_board() {
board.resize(BOARD_SIZE);
for (int y = 0; y < BOARD_SIZE; y++) {
board[y].resize(BOARD_SIZE);
}
array<string> white_pieces = {"R", "N", "B", "Q", "K", "B", "N", "R"};
array<string> black_pieces = {"r", "n", "b", "q", "k", "b", "n", "r"};
for (int i = 0; i < BOARD_SIZE; i++) {
board[0][i] = black_pieces[i];
board[1][i] = "p";
board[6][i] = "P";
board[7][i] = white_pieces[i];
}
for (int y = 2; y < 6; y++) {
for (int x = 0; x < BOARD_SIZE; x++) {
board[y][x] = ".";
}
}
}

// --- Save and Load Functions ---


void save_game() {
string filename = save_file_dialog("NVGT Chess Save (*.json):json", cwdir());
if (filename.empty()) {
speak("Save cancelled.", true);
return;
}

if (!filename.ends_with(".json")) {
filename += ".json";
}

json_object@ root = json_object();

// Board state
json_array@ board_json = json_array();
for (int y = 0; y < BOARD_SIZE; y++) {
json_array@ row = json_array();
for (int x = 0; x < BOARD_SIZE; x++) {
row.add(board[y][x]);
}
board_json.add(row);
}
root["board"] = board_json;
// Game state
json_object@ state = json_object();
state["player_is_white"] = player_is_white;
state["vs_computer"] = vs_computer;
state["is_human_white"] = is_human_white;
state["fifty_move_counter"] = fifty_move_counter;
state["en_passant_x"] = en_passant_x;
state["en_passant_y"] = en_passant_y;
state["ai_search_depth"] = AI_SEARCH_DEPTH; // --- NEW: Save AI difficulty
root["state"] = state;

// Castling Rights
json_object@ rights = json_object();
rights["white_king_moved"] = white_king_moved;
rights["black_king_moved"] = black_king_moved;
rights["white_rook_a_moved"] = white_rook_a_moved;
rights["white_rook_h_moved"] = white_rook_h_moved;
rights["black_rook_a_moved"] = black_rook_a_moved;
rights["black_rook_h_moved"] = black_rook_h_moved;
root["rights"] = rights;

// Position History for Threefold Repetition


json_object@ history_json = json_object();
string[]@ keys = position_history.get_keys();
for (uint i = 0; i < keys.length(); i++) {
int count = 0;
position_history.get(keys[i], count);
history_json[keys[i]] = count;
}
root["position_history"] = history_json;

// Write to file
string json_string = root.stringify(2);
if (file_put_contents(filename, json_string)) {
speak("Game saved successfully.", true);
} else {
speak("Error: Could not save game to file.", true);
}
}

bool load_game_from_file(const string&in filename) {


string json_string = file_get_contents(filename);
if (json_string.empty()) {
speak("Error: Save file is empty or could not be read.", true);
return false;
}

try {
var@ data = parse_json(json_string);
json_object@ root = cast<json_object@>(data);
if (@root is null) throw("Invalid save file format.");

json_array@ board_json = cast<json_array@>(root["board"]);


if (@board_json is null || board_json.size() != BOARD_SIZE) throw("Invalid
board data in save file.");
init_board();
for (uint y = 0; y < BOARD_SIZE; y++) {
json_array@ row_json = cast<json_array@>(board_json[y]);
if (@row_json is null || row_json.size() != BOARD_SIZE) throw("Invalid
board row data.");
for (uint x = 0; x < BOARD_SIZE; x++) {
board[y][x] = string(row_json[x]);
}
}

json_object@ state = cast<json_object@>(root["state"]);


if (@state is null) throw("Missing game state data.");
player_is_white = bool(state["player_is_white"]);
vs_computer = bool(state["vs_computer"]);
is_human_white = bool(state["is_human_white"]);
fifty_move_counter = int(state["fifty_move_counter"]);
en_passant_x = int(state["en_passant_x"]);
en_passant_y = int(state["en_passant_y"]);
AI_SEARCH_DEPTH = int(state["ai_search_depth"]); // --- NEW: Load AI
difficulty ---

json_object@ rights = cast<json_object@>(root["rights"]);


if (@rights is null) throw("Missing castling rights data.");
white_king_moved = bool(rights["white_king_moved"]);
black_king_moved = bool(rights["black_king_moved"]);
white_rook_a_moved = bool(rights["white_rook_a_moved"]);
white_rook_h_moved = bool(rights["white_rook_h_moved"]);
black_rook_a_moved = bool(rights["black_rook_a_moved"]);
black_rook_h_moved = bool(rights["black_rook_h_moved"]);

json_object@ history_json = cast<json_object@>(root["position_history"]);


if(@history_json is null) throw("Missing position history data.");
@position_history = dictionary();
string[]@ keys = history_json.get_keys();
for (uint i = 0; i < keys.length(); i++) {
position_history.set(keys[i], int(history_json[keys[i]]));
}

} catch {
speak("Error parsing save file: " + get_exception_info(), true);
return false;
}

return true;
}

// --- End Save and Load Functions ---

// ... (The next section of code is identical until play_chess())

string get_piece_name(const string &in piece) {


if (piece == "K" || piece == "k") return "king";
if (piece == "Q" || piece == "q") return "queen";
if (piece == "R" || piece == "r") return "rook";
if (piece == "B" || piece == "b") return "bishop";
if (piece == "N" || piece == "n") return "knight";
if (piece == "P" || piece == "p") return "pawn";
return "empty";
}

enum ChessState { SELECT, MOVE }


ChessState chess_state = SELECT;
int last_cursor_x = -1;
int last_cursor_y = -1;

enum PieceColor { NONE, WHITE, BLACK }


PieceColor get_piece_color(const string &in piece) {
if (piece == "P" || piece == "N" || piece == "B" || piece == "R" || piece ==
"Q" || piece == "K") return WHITE;
if (piece == "p" || piece == "n" || piece == "b" || piece == "r" || piece ==
"q" || piece == "k") return BLACK;
return NONE;
}

bool is_valid_move(int from_x, int from_y, int to_x, int to_y, PieceColor
moving_color_override = NONE) {
if (from_x < 0 || from_y < 0) return false;
if (from_x == to_x && from_y == to_y) return false;
string piece = board[from_y][from_x];
string target = board[to_y][to_x];
if (target == "K" || target == "k") return false;
if (piece == ".") return false;
PieceColor color = get_piece_color(piece);
PieceColor target_color = get_piece_color(target);
PieceColor allowed = moving_color_override != NONE ? moving_color_override :
(player_is_white ? WHITE : BLACK);
if (color != allowed) return false;
if (color == target_color && target_color != NONE) return false;
int dx = to_x - from_x;
int dy = to_y - from_y;
if (piece == "P" || piece == "p") {
int dir = (piece == "P") ? -1 : 1;
int start_row = (piece == "P") ? 6 : 1;
if (dx == 0 && target == ".") {
if (dy == dir) return true;
if (from_y == start_row && dy == 2 * dir && board[from_y + dir][from_x]
== "." && board[to_y][to_x] == ".") return true;
}
if ((dx == 1 || dx == -1) && dy == dir) {
if (target != "." && color != target_color) return true;
if (to_x == en_passant_x && to_y == en_passant_y) return true;
}
return false;
}
if ((piece == "K" && !white_king_moved) || (piece == "k" && !black_king_moved))
{
if (abs(dx) == 2 && dy == 0) {
if (is_king_in_check(player_is_white)) return false; // Can't castle
out of check
if (dx == 2) { // Kingside
if (piece == "K" && !white_rook_h_moved && board[from_y][from_x+1]
== "." && board[from_y][from_x+2] == "."){
// Check if squares king moves over are attacked
board[from_y][from_x+1] = "K"; board[from_y][from_x] = ".";
if(is_king_in_check(true)) { board[from_y][from_x+1] = ".";
board[from_y][from_x] = "K"; return false; }
board[from_y][from_x+2] = "K"; board[from_y][from_x+1] = ".";
if(is_king_in_check(true)) { board[from_y][from_x+2] = ".";
board[from_y][from_x] = "K"; return false; }
board[from_y][from_x] = "K"; board[from_y][from_x+2] = ".";
return true;
}
if (piece == "k" && !black_rook_h_moved && board[from_y][from_x+1]
== "." && board[from_y][from_x+2] == "."){
board[from_y][from_x+1] = "k"; board[from_y][from_x] = ".";
if(is_king_in_check(false)) { board[from_y][from_x+1] = ".";
board[from_y][from_x] = "k"; return false; }
board[from_y][from_x+2] = "k"; board[from_y][from_x+1] = ".";
if(is_king_in_check(false)) { board[from_y][from_x+2] = ".";
board[from_y][from_x] = "k"; return false; }
board[from_y][from_x] = "k"; board[from_y][from_x+2] = ".";
return true;
}
}
if (dx == -2) { // Queenside
if (piece == "K" && !white_rook_a_moved && board[from_y][from_x-1]
== "." && board[from_y][from_x-2] == "." && board[from_y][from_x-3] == "."){
board[from_y][from_x-1] = "K"; board[from_y][from_x] = ".";
if(is_king_in_check(true)) { board[from_y][from_x-1] = ".";
board[from_y][from_x] = "K"; return false; }
board[from_y][from_x-2] = "K"; board[from_y][from_x-1] = ".";
if(is_king_in_check(true)) { board[from_y][from_x-2] = ".";
board[from_y][from_x] = "K"; return false; }
board[from_y][from_x] = "K"; board[from_y][from_x-2] = ".";
return true;
}
if (piece == "k" && !black_rook_a_moved && board[from_y][from_x-1]
== "." && board[from_y][from_x-2] == "." && board[from_y][from_x-3] == "."){
board[from_y][from_x-1] = "k"; board[from_y][from_x] = ".";
if(is_king_in_check(false)) { board[from_y][from_x-1] = ".";
board[from_y][from_x] = "k"; return false; }
board[from_y][from_x-2] = "k"; board[from_y][from_x-1] = ".";
if(is_king_in_check(false)) { board[from_y][from_x-2] = ".";
board[from_y][from_x] = "k"; return false; }
board[from_y][from_x] = "k"; board[from_y][from_x-2] = ".";
return true;
}
}
}
}
if (piece == "R" || piece == "r") {
if (dx != 0 && dy != 0) return false;
int step_x = dx == 0 ? 0 : (dx > 0 ? 1 : -1);
int step_y = dy == 0 ? 0 : (dy > 0 ? 1 : -1);
int x = from_x + step_x, y = from_y + step_y;
while (x != to_x || y != to_y) {
if (board[y][x] != ".") return false;
x += step_x; y += step_y;
}
return true;
}
if (piece == "N" || piece == "n") {
if ((abs(dx) == 2 && abs(dy) == 1) || (abs(dx) == 1 && abs(dy) == 2))
return true;
return false;
}
if (piece == "B" || piece == "b") {
if (abs(dx) != abs(dy)) return false;
int step_x = dx > 0 ? 1 : -1;
int step_y = dy > 0 ? 1 : -1;
int x = from_x + step_x, y = from_y + step_y;
while (x != to_x && y != to_y) {
if (board[y][x] != ".") return false;
x += step_x; y += step_y;
}
return true;
}
if (piece == "Q" || piece == "q") {
if (dx == 0 || dy == 0) {
int step_x = dx == 0 ? 0 : (dx > 0 ? 1 : -1);
int step_y = dy == 0 ? 0 : (dy > 0 ? 1 : -1);
int x = from_x + step_x, y = from_y + step_y;
while (x != to_x || y != to_y) {
if (board[y][x] != ".") return false;
x += step_x; y += step_y;
}
return true;
} else if (abs(dx) == abs(dy)) {
int step_x = dx > 0 ? 1 : -1;
int step_y = dy > 0 ? 1 : -1;
int x = from_x + step_x, y = from_y + step_y;
while (x != to_x && y != to_y) {
if (board[y][x] != ".") return false;
x += step_x; y += step_y;
}
return true;
}
return false;
}
if (piece == "K" || piece == "k") {
if (abs(dx) <= 1 && abs(dy) <= 1) return true;
return false;
}
return false;
}

string get_board_state_string() {
string state = "";
for (int y = 0; y < BOARD_SIZE; y++) {
for (int x = 0; x < BOARD_SIZE; x++) {
state += board[y][x];
}
}
state += (player_is_white ? "w" : "b");
string castle_rights = "";
if (!white_king_moved && !white_rook_h_moved) castle_rights += "K";
if (!white_king_moved && !white_rook_a_moved) castle_rights += "Q";
if (!black_king_moved && !black_rook_h_moved) castle_rights += "k";
if (!black_king_moved && !black_rook_a_moved) castle_rights += "q";
state += (castle_rights.empty() ? "-" : castle_rights);
if (en_passant_x != -1) {
state += string("abcdefgh")[en_passant_x];
state += (8 - en_passant_y);
} else {
state += "-";
}
return state;
}

bool check_for_insufficient_material() {
int white_knights = 0, white_bishops = 0, white_others = 0;
int black_knights = 0, black_bishops = 0, black_others = 0;
array<int> white_bishop_squares, black_bishop_squares;

for (int y = 0; y < BOARD_SIZE; y++) {


for (int x = 0; x < BOARD_SIZE; x++) {
string p = board[y][x];
if (p == "N") white_knights++;
else if (p == "B") {
white_bishops++;
white_bishop_squares.insert_last((x + y) % 2);
} else if (p == "P" || p == "R" || p == "Q") white_others++;
else if (p == "n") black_knights++;
else if (p == "b") {
black_bishops++;
black_bishop_squares.insert_last((x + y) % 2);
} else if (p == "p" || p == "r" || p == "q") black_others++;
}
}

if (white_others > 0 || black_others > 0) return false;


if (white_knights == 0 && white_bishops == 0 && black_knights == 0 &&
black_bishops == 0) return true;
if (white_knights + white_bishops <= 1 && black_knights == 0 && black_bishops
== 0) return true;
if (black_knights + black_bishops <= 1 && white_knights == 0 && white_bishops
== 0) return true;
if (white_knights == 0 && white_bishops == 1 && black_knights == 0 &&
black_bishops == 1) {
if (white_bishop_squares[0] == black_bishop_squares[0]) return true;
}

return false;
}

void perform_move(int from_x, int from_y, int to_x, int to_y) {


string moving_piece = board[from_y][from_x];
string captured_piece = board[to_y][to_x];
bool is_en_passant_move = (moving_piece == "P" || moving_piece == "p") && to_x
== en_passant_x && to_y == en_passant_y && captured_piece == ".";
bool is_pawn_move = (moving_piece == "P" || moving_piece == "p");
bool is_capture = (captured_piece != ".") || is_en_passant_move;

if (is_pawn_move || is_capture) {
fifty_move_counter = 0;
if(@position_history !is null) position_history.delete_all();
} else {
fifty_move_counter++;
}

if (is_capture) {
playChessSound("capture.wav");
} else {
playChessSound("drop_piece.wav");
}

int from_rank = 8 - from_y;


int to_rank = 8 - to_y;
string from_pos = string("ABCDEFGH").substr(from_x, 1) + from_rank;
string to_pos = string("ABCDEFGH").substr(to_x, 1) + to_rank;
PieceColor moving_color = get_piece_color(moving_piece);
string moving_color_name = (moving_color == WHITE) ? "White " : "Black ";

if (is_en_passant_move) {
// Handle the special case for en passant, where the captured piece is not
on the target square.
string captured_piece_name = "pawn";
string captured_color_name = (moving_color == WHITE) ? "Black " : "White ";
speak(moving_color_name + get_piece_name(moving_piece) + " captures " +
captured_color_name + captured_piece_name + " en passant at " + to_pos + ".",
false);
} else if (is_capture) {
// Handle a standard capture.
PieceColor captured_color = get_piece_color(captured_piece);
string captured_color_name = (captured_color == WHITE) ? "White " :
(captured_color == BLACK) ? "Black " : "";
speak(moving_color_name + get_piece_name(moving_piece) + " captures " +
captured_color_name + get_piece_name(captured_piece) + " at " + to_pos + ".",
false);
} else {
// Handle a simple move with no capture.
speak("Moved " + moving_color_name + get_piece_name(moving_piece) + " from
" + from_pos + " to " + to_pos + ".", false);
}

board[to_y][to_x] = board[from_y][from_x];
board[from_y][from_x] = ".";

if ((moving_piece == "K" || moving_piece == "k") && abs(to_x - from_x) == 2) {


playChessSound("castling.wav");
if (to_x > from_x) {
board[to_y][to_x - 1] = board[to_y][to_x + 1];
board[to_y][to_x + 1] = ".";
speak(moving_color_name + "castles kingside.", false);
} else {
board[to_y][to_x + 1] = board[to_y][to_x - 2];
board[to_y][to_x - 2] = ".";
speak(moving_color_name + "castles queenside.", false);
}
}

if (is_en_passant_move) {
playChessSound("en_passant.wav");
int cap_y = (moving_piece == "P") ? to_y + 1 : to_y - 1;
board[cap_y][to_x] = ".";
}

if (moving_piece == "K") white_king_moved = true;


if (moving_piece == "k") black_king_moved = true;
if (moving_piece == "R" && from_x == 0 && from_y == 7) white_rook_a_moved =
true;
if (moving_piece == "R" && from_x == 7 && from_y == 7) white_rook_h_moved =
true;
if (moving_piece == "r" && from_x == 0 && from_y == 0) black_rook_a_moved =
true;
if (moving_piece == "r" && from_x == 7 && from_y == 0) black_rook_h_moved =
true;
if ((moving_piece == "P" && from_y - to_y == 2) || (moving_piece == "p" && to_y
- from_y == 2)) {
en_passant_x = to_x;
en_passant_y = (from_y + to_y) / 2;
} else {
en_passant_x = -1;
en_passant_y = -1;
}

if ((moving_piece == "P" && to_y == 0) || (moving_piece == "p" && to_y == 7)) {


string promo = "";
bool is_ai_turn = vs_computer && (player_is_white != is_human_white);
if (is_ai_turn) {
promo = player_is_white ? "Q" : "q";
} else {
promo = choose_promotion(moving_piece == "P");
}
board[to_y][to_x] = promo;
speak(moving_color_name + "pawn promotes to " + get_piece_name(promo) + "
on " + to_pos + ".", false);
}

string end_reason = "";


bool opponent_has_moves = player_has_legal_moves(!player_is_white);
bool opponent_in_check = is_king_in_check(!player_is_white);

if (opponent_in_check && !opponent_has_moves) {


game_over = true;
end_reason = (player_is_white ? "White" : "Black") + " wins by checkmate!";
playChessSound("you_won.wav");
} else if (!opponent_in_check && !opponent_has_moves) {
game_over = true;
end_reason = "Draw by stalemate.";
playChessSound("drawn.wav");
} else if (fifty_move_counter >= 100) {
game_over = true;
end_reason = "Draw by the fifty-move rule.";
playChessSound("drawn.wav");
} else if (is_capture && check_for_insufficient_material()) {
game_over = true;
end_reason = "Draw by insufficient material.";
playChessSound("drawn.wav");
}

player_is_white = !player_is_white;

if (!game_over) {
string current_state = get_board_state_string();
int count = 0;
if(position_history.get(current_state, count)) {
count++;
position_history.set(current_state, count);
} else {
count = 1;
position_history.set(current_state, 1);
}
if (count >= 3) {
game_over = true;
end_reason = "Draw by threefold repetition.";
playChessSound("drawn.wav");
}
}

if (game_over) {
playChessSound("game_over.wav");
speak(end_reason, true);
return;
}

if (is_king_in_check(player_is_white)) {
playChessSound("check.wav");
speak((player_is_white ? "White" : "Black") + " is in check.", false);
}

speak((player_is_white ? "White's turn." : "Black's turn."), false);

chess_state = SELECT;
selected_x = -1;
selected_y = -1;

// --- MODIFIED: Update cursor position based on the next player's perspective
---
// In human-vs-human mode, this flips the board view for each player's turn.
// The player_is_white variable has already been flipped to the next player.
bool human_view_is_white = vs_computer ? is_human_white : player_is_white;
cursor_x = to_x;
cursor_y = human_view_is_white ? 7 - to_y : to_y;
last_cursor_x = cursor_x;
last_cursor_y = cursor_y;}

string choose_promotion(bool is_white) {


playChessSound("request_promotion.wav");
activateMenuTouchControls();
menu@ m = createDefaultMenu((is_white ? "White" : "Black") + " pawn promotion!
Use up and down arrows to choose, enter to confirm.");

@m.pack_file = @gChessSoundPack;
if (@m.pack_file is null) @m.pack_file = @gMainMenuPack;

m.open_sound = "menu_open.wav";
m.select_sound = "promotion.wav";
m.close_sound = "score_list_close.wav";

m.add_item("Queen", is_white ? "Q" : "q");


m.add_item("Rook", is_white ? "R" : "r");
m.add_item("Bishop", is_white ? "B" : "b");
m.add_item("Knight", is_white ? "N" : "n");
string selected_id = "";
while (m.monitor()) {
wait(5);
gTouchManager.monitor();
gMainMenuMusic.loop(ticks());
if (key_pressed(KEY_ESCAPE)) {
selected_id = is_white ? "Q" : "q";
playChessSound(m.close_sound);
break;
}
}
if (selected_id == "") selected_id = m.selected_item_id;
activateChessTouchControls();
playChessSound(m.select_sound);
return selected_id;
}

// --- MODIFIED: `play_chess` to include AI difficulty selection ---


void play_chess() {
@gChessSoundPack = pack_file();
if (!gChessSoundPack.open(board_pack)) {
speak("Warning: Chess could not open its sound pack. Sounds may not play.",
false);
@gChessSoundPack = null;
}

activateChessTouchControls();
bool game_loaded = false;

menu@ modeMenu = createDefaultMenu("Select Game Mode");


modeMenu.add_item("Play against a Human", "human");
modeMenu.add_item("Play against the Computer", "ai");
modeMenu.add_item("Load Saved Game", "load");
string mode_id = "";
while(modeMenu.monitor()) {
wait(5);
gTouchManager.monitor();
gMainMenuMusic.loop(ticks());
if(key_pressed(KEY_ESCAPE)) {
if (askYesNo("Exit Game?", "Are you sure you want to exit the game?",
true)) {
game_over = true;
}
}
}
if (mode_id == "") mode_id = modeMenu.selected_item_id;

if (mode_id == "load") {
string filename = open_file_dialog("NVGT Chess Save (*.json):json",
cwdir());
if (!filename.empty()) {
if (load_game_from_file(filename)) {
game_loaded = true;
game_over = false;
speak("Game loaded successfully.", true);
} else {
return;
}
} else {
return;
}
}

if (!game_loaded) {
if (mode_id == "ai") {
vs_computer = true;

// --- NEW: AI Difficulty Selection Menu ---


menu@ diffMenu = createDefaultMenu("Select AI Difficulty");
diffMenu.add_item("Easy (looks 1 move ahead)", "1");
diffMenu.add_item("Medium (looks 3 moves ahead)", "3");
diffMenu.add_item("Hard (looks 4 moves ahead)", "4");
string diff_id = "";
while(diffMenu.monitor()) {
wait(5);
gTouchManager.monitor();
gMainMenuMusic.loop(ticks());
if(key_pressed(KEY_ESCAPE)) { diff_id = "3";
playMainSound(diffMenu.close_sound); break; }
}
if(diff_id == "") diff_id = diffMenu.selected_item_id;
AI_SEARCH_DEPTH = parse_int(diff_id);
// --- End AI Difficulty Menu ---

menu@ colorMenu = createDefaultMenu("Choose your color");


colorMenu.add_item("Play as White", "white");
colorMenu.add_item("Play as Black", "black");
string color_id = "";
while(colorMenu.monitor()) {
wait(5);
gTouchManager.monitor();
gMainMenuMusic.loop(ticks());
if(key_pressed(KEY_ESCAPE)) { color_id = "white";
playMainSound(colorMenu.close_sound); break; }
}
if (color_id == "") color_id = colorMenu.selected_item_id;
is_human_white = (color_id == "white");
} else {
vs_computer = false;
is_human_white = true;
}

player_is_white = true;
init_board();

game_over = false;
fifty_move_counter = 0;
@position_history = dictionary();
position_history.set(get_board_state_string(), 1);
}

// --- MODIFIED: Cursor now starts at the bottom-left of the current view.
cursor_x = 0;
cursor_y = 0;

chess_state = SELECT;
last_cursor_x = -1;
last_cursor_y = -1;
piece_selected = false;
selected_x = -1;
selected_y = -1;

playChessSound("start_game.wav");
if (!game_loaded) {
speak("Welcome to NVGT Chess. Use arrow keys to move, F1 for help, F2 to
save, and escape to quit.", true); wait(2200);
}
speak((player_is_white ? "White's turn." : "Black's turn."), false);
while (!game_over) {
// --- MODIFIED: Determine view perspective based on the current player's
turn.
// This makes the board flip in human-vs-human games.
bool human_view_is_white = vs_computer ? is_human_white : player_is_white;

bool is_ai_turn = vs_computer && (player_is_white != is_human_white);

if (is_ai_turn) {
wait(500);
speak("Computer is thinking...", true);
wait(100);
dictionary@ move = find_best_move(player_is_white);
if (@move !is null) {
int from_x = 0, from_y = 0, to_x = 0, to_y = 0;
move.get("from_x", from_x);
move.get("from_y", from_y);
move.get("to_x", to_x);
move.get("to_y", to_y);
perform_move(from_x, from_y, to_x, to_y);
} else {
bool in_check = is_king_in_check(player_is_white);
if (in_check) {
speak("The computer is in checkmate. " + (is_human_white ?
"White" : "Black") + " wins!", true);
} else {
speak("The computer is in stalemate. The game is a draw.",
true);
}
game_over = true;
}
continue;
}

gTouchManager.monitor();

if (key_pressed(KEY_F1)) {
string turn_info = "Current turn: " + (player_is_white ? "White" :
"Black") + ". ";
string state_info = (chess_state == SELECT) ? "Waiting to select a
piece." : "Waiting to move the selected piece.";
speak(turn_info + state_info, true);
} else if (key_pressed(KEY_F2)) {
save_game();
}

int display_y = human_view_is_white ? 7 - cursor_y : cursor_y;


if (cursor_x != last_cursor_x || cursor_y != last_cursor_y) {

bool is_drop_target = (chess_state == MOVE && is_valid_move(selected_x,


selected_y, cursor_x, display_y));

if (is_drop_target) {
playChessSound("drop_target.wav");
} else {
if ((cursor_x + display_y) % 2 == 0) {
playChessSound("black_square.wav");
}
}
string piece = board[display_y][cursor_x];
string name = get_piece_name(piece);
PieceColor color = get_piece_color(piece);
string color_name = (color == WHITE) ? "White " : (color == BLACK) ?
"Black " : "";
string pos = " " + string("ABCDEFGH").substr(cursor_x, 1) + " " +
(human_view_is_white ? cursor_y + 1 : 8 - cursor_y);
bool is_player_piece = (player_is_white && color == WHITE) || (!
player_is_white && color == BLACK);

if (is_drop_target) {
if (piece == ".") {
speak("Square drop target" + pos, true);
} else {
speak("Square drop target " + color_name + name + pos, true);
}
} else {
if (piece == ".") {
speak("Empty square" + pos, true);
} else {
if (chess_state == SELECT && is_player_piece) {
speak(color_name + name + " draggable" + pos, true);
} else {
speak(color_name + name + pos, true);
}
}
}
last_cursor_x = cursor_x;
last_cursor_y = cursor_y;
}

// --- MODIFIED: Key handling now respects the flipped board view for Black
player ---
if (human_view_is_white) {
// White's (or default) perspective
if (key_pressed(KEY_UP) && cursor_y < 7) cursor_y++;
if (key_pressed(KEY_DOWN) && cursor_y > 0) cursor_y--;
if (key_pressed(KEY_LEFT) && cursor_x > 0) cursor_x--;
if (key_pressed(KEY_RIGHT) && cursor_x < BOARD_SIZE - 1) cursor_x++;
} else {
// Black's perspective (board is flipped)
if (key_pressed(KEY_UP) && cursor_y < 7) cursor_y++;
if (key_pressed(KEY_DOWN) && cursor_y > 0) cursor_y--;
if (key_pressed(KEY_LEFT) && cursor_x < BOARD_SIZE - 1) cursor_x++;
if (key_pressed(KEY_RIGHT) && cursor_x > 0) cursor_x--;
}

if (chess_state == SELECT && piece_selected) {


speak("Piece selected. Use arrows to choose destination, enter to
drop.", true);
piece_selected = false;
}
wait(50);
if (key_pressed(KEY_ESCAPE)) {
if (chess_state == MOVE) {
chess_state = SELECT;
selected_x = -1;
selected_y = -1;
last_cursor_x = -1;
last_cursor_y = -1;
speak("Selection cancelled.", false);
} else {
if (askYesNo("Exit Game?", "Are you sure you want to exit the
game?", true)) {
game_over = true;
}
}
}

if (key_pressed(KEY_RETURN)) {
int board_y = human_view_is_white ? 7 - cursor_y : cursor_y;
if (chess_state == SELECT) {
string piece = board[board_y][cursor_x];
PieceColor color = get_piece_color(piece);
if (piece != "." && ((player_is_white && color == WHITE) || (!
player_is_white && color == BLACK))) {
selected_x = cursor_x;
selected_y = board_y;
chess_state = MOVE;
playChessSound("pick_piece.wav");
speak("Selected " + get_piece_name(piece) + ". Use arrows to
choose destination, enter to drop.", true);
} else {
playChessSound("invalid.wav");
}
} else if (chess_state == MOVE) {
int to_y = human_view_is_white ? 7 - cursor_y : cursor_y;
if (is_valid_move(selected_x, selected_y, cursor_x, to_y)) {
perform_move(selected_x, selected_y, cursor_x, to_y);
} else {
playChessSound("invalid.wav");
speak("Invalid move. Selection cancelled. Select a piece.",
false);
chess_state = SELECT;
selected_x = -1;
selected_y = -1;
last_cursor_x = -1;
last_cursor_y = -1;
}
}
}
}

if (key_pressed(KEY_ESCAPE)) {
speak("Game exited.", true);
} else {
speak("Game over.", true);
wait(2000);
}

if (@gChessSoundPack !is null) {


gChessSoundPack.close();
@gChessSoundPack = null;
}
if (@gChessSoundHandle !is null) {
gChessSoundHandle.close();
@gChessSoundHandle = null;
}
activateMenuTouchControls();
}

You might also like