int main(int argc, const char *argv[]) { int rc = 1; try { LoadIniFile("ttychess.ini"); TheTerminal.printline(); TheTerminal.printline(std::string("Teletype chess program (") + ConvertDateToVersion(__DATE__) + ") by Don Cross."); TheTerminal.printline("http://cosinekitty.com/chenard"); while (true) { ChessBoard board; ChessUI_TTY ui; ChessGame game(board, ui); ui.SetGame(&game); game.Play(); ui.SetGame(NULL); } rc = 0; } catch (const char *message) { TheTerminal.print(message); TheTerminal.printline(); } TheTerminal.close(); return rc; }
virtual ChessPlayer *CreatePlayer(ChessSide side) { while (humanSide == SIDE_NEITHER) { TheTerminal.print("Which side do you want to play (w, b, quit)? "); std::string choice = TheTerminal.readline(); if (choice == "w") { humanSide = SIDE_WHITE; } else if (choice == "b") { humanSide = SIDE_BLACK; } else if (choice == "quit") { throw "Chess program exited."; } } if (side == humanSide) { human = new HumanChessPlayer(*this); return human; } else { computer = new ComputerChessPlayer(*this); computer->SetTimeLimit(ComputerThinkTime); computer->SetSearchBias(1); computer->setResignFlag(false); return computer; } }
//-------------------------------------------------------------- // Called whenever a player needs to be asked which // piece a pawn should be promoted to. // Must return one of the following values: // Q_INDEX, R_INDEX, B_INDEX, N_INDEX //-------------------------------------------------------------- virtual SQUARE PromotePawn(int pawnDest, ChessSide side) { while (true) { TheTerminal.print("Promote pawn to Q, R, N, B? "); std::string answer = TheTerminal.readline(); if (answer == "q") { return Q_INDEX; } else if (answer == "r") { return R_INDEX; } else if (answer == "n") { return N_INDEX; } else if (answer == "b") { return B_INDEX; } } }
//---------------------------------------------------------------- // The following member function displays a prompt and waits // for acknowledgement from the user. //---------------------------------------------------------------- virtual void NotifyUser ( const char *message ) { TheTerminal.print(message); }
void ReallyDrawBoard(const ChessBoard &board) { // Show the board from the human player's point of view. char startFile, startRank; int deltaFile, deltaRank; if (humanSide == SIDE_WHITE) { // We draw the board with White (rank 1) at the bottom, so we start with rank 8. startFile = 'a'; startRank = '8'; deltaFile = +1; deltaRank = -1; } else { // We draw the board with Black (rank 8) at the bottom, so we start with rank 1. startFile = 'h'; startRank = '1'; deltaFile = -1; deltaRank = +1; } for (int y=0, rank=startRank; y < 8; ++y, rank += deltaRank) { TheTerminal.print(static_cast<char>(rank)); TheTerminal.print(" "); for (int x=0, file=startFile; x < 8; ++x, file += deltaFile) { const SQUARE square = board.GetSquareContents(file - 'a', rank - '1'); const char *text; switch (square) { case EMPTY: text = ". "; break; case WPAWN: text = "WP"; break; case WKNIGHT: text = "WN"; break; case WBISHOP: text = "WB"; break; case WROOK: text = "WR"; break; case WQUEEN: text = "WQ"; break; case WKING: text = "WK"; break; case BPAWN: text = "BP"; break; case BKNIGHT: text = "BN"; break; case BBISHOP: text = "BB"; break; case BROOK: text = "BR"; break; case BQUEEN: text = "BQ"; break; case BKING: text = "BK"; break; default: text = "??"; break; } TheTerminal.print(text); if (x < 7) // no need for white space at end of line { TheTerminal.print(" "); } } TheTerminal.printline(); TheTerminal.printline(); } // Print file letters at the bottom. TheTerminal.print(" "); for (int x=0, file=startFile; x < 8; ++x, file += deltaFile) { TheTerminal.print(static_cast<char>(file)); if (x < 7) // no need for white space at end of line { TheTerminal.print(" "); } } TheTerminal.printline(); TheTerminal.printline(); }
//---------------------------------------------------------------- // The following function is called by the ChessGame after // each player moves, so that the move can be displayed // in standard chess notation, if the UI desires. // It is helpful to use the ::FormatChessMove() function // for this purpose. // The parameter 'thinkTime' is how long the player thought // about the move, expressed in hundredths of seconds. // // NOTE: This function is called BEFORE the move // has been made on the given ChessBoard. // This is necessary for ::FormatChessMove() to work. //---------------------------------------------------------------- virtual void RecordMove(ChessBoard &board, Move move, INT32 thinkTime) { TheTerminal.print(BoolSideName(board.WhiteToMove())); TheTerminal.print(" move #"); TheTerminal.printInteger((board.GetCurrentPlyNumber()/2) + 1); TheTerminal.print(": "); int source, dest; SQUARE prom = move.actualOffsets(board, source, dest); char sourceFile = XPART(source) - 2 + 'a'; char sourceRank = YPART(source) - 2 + '1'; char destFile = XPART(dest) - 2 + 'a'; char destRank = YPART(dest) - 2 + '1'; TheTerminal.print(sourceFile); TheTerminal.print(sourceRank); TheTerminal.print(destFile); TheTerminal.print(destRank); if (prom) { char pchar = '?'; int pindex = UPIECE_INDEX(prom); switch (pindex) { case Q_INDEX: pchar = 'Q'; break; case R_INDEX: pchar = 'R'; break; case B_INDEX: pchar = 'B'; break; case N_INDEX: pchar = 'N'; break; } TheTerminal.print(pchar); } // Temporarily make the move, so we can display what the board // looks like after the move has been made. // We also use this to determine if the move causes check, // checkmate, stalemate, etc. UnmoveInfo unmove; board.MakeMove(move, unmove); // Report check, checkmate, draws. bool inCheck = board.CurrentPlayerInCheck(); bool canMove = board.CurrentPlayerCanMove(); if (canMove) { if (inCheck) { TheTerminal.print('+'); } } else { // The game is over! if (inCheck) { if (board.WhiteToMove()) { // White just got checkmated. TheTerminal.print("# 0-1"); } else { // Black just got checkmated. TheTerminal.print("# 1-0"); } } else { // The game is a draw (could be stalemate, could be by repetition, insufficient material, ...) TheTerminal.print(" 1/2-1/2"); } } TheTerminal.printline(); // Unmake the move, because the caller will make the move itself. board.UnmakeMove(move, unmove); }
//-------------------------------------------------------------- // The following function should return true if the Move // was read successfully, or false to abort the game. // The move does not have to be legal; if an illegal move // is returned, the caller will repeatedly call until a // legal move is obtained. //-------------------------------------------------------------- virtual bool ReadMove ( ChessBoard &board, int &source, int &dest, SQUARE &promPieceIndex ) // set to 0 (P_INDEX) if no promotion, or call to PromotePawn is needed; set to N_INDEX..Q_INDEX to specify promotion. { promPieceIndex = P_INDEX; bool boardWasAlreadyPrinted = AutoPrintBoard; // accept either of the following two formats: e2e4, g7g8q while (true) { if (IsFirstPrompt) { IsFirstPrompt = false; TheTerminal.printline("Enter 'help' for info and more options."); } TheTerminal.print("Your move? "); std::string line = TheTerminal.readline(); // ================================================================= // REMEMBER: Update "PrintHelp()" every time you add a command! // ================================================================= if (line == "board") { ReallyDrawBoard(board); boardWasAlreadyPrinted = true; } else if (line == "help") { PrintHelp(); } else if (line == "new") { if (human) { human->SetQuitReason(qgr_startNewGame); // suppress saying that the player resigned } return false; // Start a new game. } else if (line == "quit") { throw "Chess program exited."; } else if (line == "show") { AutoPrintBoard = true; if (!boardWasAlreadyPrinted) { ReallyDrawBoard(board); boardWasAlreadyPrinted = true; } } else if (line == "swap") { if (game) { humanSide = OppositeSide(humanSide); if (humanSide == SIDE_WHITE) { game->SetPlayers(human, computer); } else { game->SetPlayers(computer, human); } TheTerminal.print("You are now playing as "); TheTerminal.print(SideName(humanSide)); TheTerminal.printline("."); source = 0; dest = SPECIAL_MOVE_NULL; return true; } } else if (line == "time") { // Adjust computer's think time. TheTerminal.print("The computer's max think time is now "); TheTerminal.printChessTime(ComputerThinkTime); TheTerminal.printline("."); TheTerminal.print("Enter new think time (0.1 .. 600): "); std::string line = TheTerminal.readline(); double thinkTime = atof(line.c_str()); if ((thinkTime >= 0.1) && (thinkTime < 600.0)) { ComputerThinkTime = static_cast<int>(100.0*thinkTime + 0.5); // round seconds to integer centiseconds if (computer) { computer->SetTimeLimit(ComputerThinkTime); } } else { TheTerminal.printline("Invalid think time - ignoring."); } } else { if (ParseFancyMove(line.c_str(), board, source, dest, promPieceIndex)) { return true; } } } }
int main() { Terminal term; term.enterAltScreen().enableMouse().placeCursor(50, 50); term.setWrapAround(true); term //.setForeground(Color::BLACK) //.setBackground(Color::GREEN) .clear(); term.fillRectangle(50, 8, 60, 10, 'X'); term.placeCursor(term.columns() / 2, term.rows() - 1).print("[HELLO WORLD TOP]"); term.placeCursor(term.columns() / 2, 0).print("[HELLO WORLD BOTTOM]"); term.placeCursor(5, 5); term.print("Hello %d, %d", term.rows(), term.columns()); bool run = true; while (run) { switch (term.await()) { case EventType::KEY: switch (term.lastKey()) { case Key::UP: term.moveCursor(0, 1); break; case Key::DOWN: term.moveCursor(0, -1); break; case Key::LEFT: term.moveCursor(-1, 0); break; case Key::RIGHT: term.moveCursor(1, 0); break; case Key::F1: term.setForeground(Color::RED).print("F1"); break; case Key::F2: term.setForeground(Color::GREEN).print("F2"); break; case Key::F3: term.setForeground(Color::YELLOW).print("F3"); break; case Key::F4: term.setForeground(Color::BLUE).print("F4"); break; case Key::F5: term.setForeground(Color::MAGENTA).print("F5"); break; case Key::F6: term.setForeground(Color::CYAN).print("F6"); break; case Key::F7: term.setForeground(Color::WHITE).print("F7"); break; case Key::F8: term.setForeground(Color::RED).print("F8"); break; case Key::F9: term.setForeground(Color::GREEN).print("F9"); break; case Key::F10: term.setForeground(Color::YELLOW).print("F10"); break; case Key::F11: term.setForeground(Color::BLUE).print("F11"); break; case Key::F12: term.setForeground(Color::MAGENTA).print("F12"); break; default: break; } break; case EventType::CHAR: { uint32_t c = term.lastCharacter(); if (c == 3 || c == 4) run = false; if (c == 'A') term.placeCursorAtColumn(term.columns() - 1); else if (c == '|') term.setCursorStyleBar(); else if (c == 'B') term.setCursorStyleBlock(); else if (c == 'C') term.clear().placeCursor(40, 5).setTitle("CLEARED"); else if (c == 'D') term.deleteLines(2); else if (c == 'E') term.deleteCells(1); else if (c == 'U') term.clear() .placeCursor(5, 5).print("Just one combining: \u0302") .placeCursor(5, 6).print("'o' followed by combining: o\u0302") .placeCursor(5, 7).print("'o' followed by combining, then go back and replace: o\u0302").moveCursor(-1,0).print("U") .placeCursor(5, 8).print("'o' last column followed by combining:").placeCursor(term.columns() - 1, 8).print("o\u0302") .placeCursor(5, 9).print("'ou', then back and combining: ou").moveCursor(-1, 0).print("\u0302") .placeCursor(5, 10).print("Double-width and combining: 枝").print("\u0302") .placeCursor(5, 11).print("Double, back 1 and combining: 枝").moveCursor(-1, 0).print("\u0302") .placeCursor(5, 12).print("Double, back 2 and combining: 枝").moveCursor(-2, 0).print("\u0302") .placeCursor(5, 13).print("枝").placeCursorAtColumn(45).placeCursorAtColumn(41).print("\u0302") .placeCursor(5, 14).print("Double-width, back 2 and a: '枝'").moveCursor(-3, 0).print("a") .placeCursor(5, 15).print("Double-width, back 1 and a: '枝'").moveCursor(-2, 0).print("a") .placeCursor(5, 16).print("Double-width in last col:").placeCursorAtColumn(term.columns()-1).print("枝") .placeCursor(5, 17).print("Single-width in last col:").placeCursorAtColumn(term.columns()-1).print("a") .placeCursor(5, 18).print("Combining in first col <=").placeCursorAtColumn(0).print("\u0302") .placeCursor(5, 19).print("a to last column and combining:").placeCursorAtColumn(term.columns()-1).print("a\u0302") .placeCursor(5, 20); else if (c == 'R') term.resetColorsAndStyle(); else if (c >= '0' && c <= '9') term.setBackground(Color(int(Color::BLACK) + (c - '0'))); else if (c == 'F') term.print("枝"); else if (c == 'I') term.placeCursorAtColumn(0); else if (c == 'L') term.insertLines(2); else if (c == 'W') term.setWrapAround(false); else if (c == 'X') term.erase(2); else if (c == 'Z') term.print("\u0302"); else if (c == '_') term.setCursorStyleUnderline(); else term.print("%c",(char) c); } break; case EventType::RESIZE: term.setTitle("RESIZE %d,%d", term.columns(), term.rows()).clear().placeCursor(term.columns()/2, 0); break; case EventType::TIMEOUT: term.print("TIMEOUT "); break; case EventType::MOUSE_DOWN: case EventType::MOUSE_UP: term.placeCursor(term.lastMouseColumn(), term.lastMouseRow()); //term.fillRectangle(term.lastMouseColumn(), term.lastMouseRow(), term.lastMouseColumn()+10, term.lastMouseRow()+3, 'X'); break; default: break; } } return 0; }