src/main.c

Tue, 13 Aug 2019 00:33:59 +0200

author
Mike Becker <universe@uap-core.de>
date
Tue, 13 Aug 2019 00:33:59 +0200
changeset 70
5427beba96d1
parent 69
c8f2c280cff7
permissions
-rw-r--r--

pgn parser can now handle comments (although it ignores them for now)

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 2016 Mike Becker. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   1. Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *
 *   2. Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions and the following disclaimer in the
 *      documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include "terminal-chess.h"
#include "game.h"
#include "input.h"
#include "colors.h"
#include <string.h>
#include <time.h>
#include <getopt.h>
#include <locale.h>

int get_settings(int argc, char **argv, Settings *settings) {
    char *valid;
    unsigned long int time, port;
    uint8_t timeunit = 60;
    size_t len;
    
    for (int opt ; (opt = getopt(argc, argv, "a:bc:hp:rsS:t:Uv")) != -1 ;) {
        switch (opt) {
        case 'c':
            settings->continuepgn = optarg;
            break;
        case 'b':
            settings->gameinfo.servercolor = BLACK;
            break;
        case 'r':
            settings->gameinfo.servercolor = rand() & 1 ? WHITE : BLACK;
            break;
        case 's':
            settings->singlemachine = 1;
            break;
        case 'S':
            settings->analyzepgn = optarg;
            break;
        case 'U':
            settings->unicode = 0;
            break;
        case 't':
        case 'a':
            len = strlen(optarg);
            if (optarg[len-1] == 's') {
                optarg[len-1] = '\0';
                timeunit = 1;
            }
            
            if ((time = strtoul(optarg, &valid, 10))*timeunit > UINT16_MAX
                || *valid != '\0') {
                fprintf(stderr, "Specified time is invalid (%s)"
                    "- Maximum: 65535 seconds (1092 minutes)\n", optarg);
                return 1;
            } else {
                settings->gameinfo.timecontrol = 1;
                if (opt=='t') {
                    settings->gameinfo.time = timeunit * time;
                } else {
                    settings->gameinfo.addtime = time;
                }
            }
            break;
        case 'p':
            port = strtol(optarg, &valid, 10);
            if (port < 1025 || port > 65535 || *valid != '\0') {
                fprintf(stderr,
                    "Invalid port number (%s) - choose a number between "
                    "1025 and 65535\n",
                    optarg);
                return 1;
            } else {
                settings->port = optarg;
            }
            break;
        case 'v':
            printf("terminal-chess : Version %s (Netcode Version %d)\n",
                PROGRAM_VERSION, NETCODE_VERSION);
            exit(0);
        case 'h':
        case '?':
            printf(
"Usage: terminal-chess [OPTION]... [HOST]\n"
"Starts/joins a network chess game\n"
"\nGeneral options\n"
"  -c <PGN file> Continue the specified game\n"
"  -h            This help page\n"
"  -p            TCP port to use (default: 27015)\n"
// TODO: implement and activate feature
//"  -S <PGN file> Compute and print statistics for the specified game\n"
"  -v            Print version information and exists\n"
"\nServer options\n"
"  -a <time>     Specifies the time to add after each move\n"
"  -b            Server plays black pieces (default: white)\n"
"  -r            Distribute color randomly\n"
"  -s            Single machine mode\n"
"  -t <time>     Specifies time limit (default: no limit)\n"
"  -U            Disables unicode pieces\n"
"\nNotes\n"
"The time unit for -a is seconds and for -t minutes by default. To "
"specify\nseconds for the -t option, use the s suffix.\n"
"Example: -t 150s\n\n"
"Use '-' for PGN files to read PGN data from standard input\n"
            );
            exit(0);
        }
    }
    
    if (optind == argc - 1) {
        settings->serverhost = argv[optind];
    } else if (optind < argc - 1) {
        fprintf(stderr, "Too many arguments\n");
        return 1;
    }
    
        
    if (settings->continuepgn) {
        if (settings->serverhost) {
            fprintf(stderr, "Can't continue a game when joining a server.\n");
            return 1;
        }
        if (settings->analyzepgn) {
            fprintf(stderr, "The options -c and -S are mutually exclusive\n");
            return 1;
        }
    }
    
    return 0;
}

static Settings default_settings() {
    Settings settings;
    memset(&settings, 0, sizeof(Settings));
    settings.gameinfo.servercolor = WHITE;
    settings.port = "27015";
    settings.unicode = !!setlocale(LC_CTYPE, "C.UTF-8");
    return settings;
}

void dump_gameinfo(GameInfo *gameinfo) {
    int serverwhite = gameinfo->servercolor == WHITE;
    attron(A_UNDERLINE);
    printw("Game details\n");
    attroff(A_UNDERLINE);
    printw("  Server:     %s\n  Client:     %s\n",
        serverwhite?"White":"Black", serverwhite?"Black":"White"
    );
    if (gameinfo->timecontrol) {
        if (gameinfo->time % 60) {
            printw("  Time limit: %ds + %ds\n",
                gameinfo->time, gameinfo->addtime);
        } else {
            printw("  Time limit: %dm + %ds\n",
                gameinfo->time/60, gameinfo->addtime);
        }
    } else {
        printw("  No time limit\n");
    }
    refresh();
}

void dump_moveinfo(GameState *gamestate) {
    int i = 1;
    for (MoveList *movelist = gamestate->movelist ;
        movelist ; movelist = movelist->next) {        
        if (++i % 2 == 0) {
            printw("%d. %s", i/2, movelist->move.string);
        } else {
            printw(" %s", movelist->move.string);
        }
        if (i % 20)  {
            addch(' ');
        } else {
            addch('\n');
        }
    }
    refresh();
}

int main(int argc, char **argv) {
    srand(time(NULL));
    
    Settings settings = default_settings();
    if (get_settings(argc, argv, &settings)) {
        return 1;
    }

    initscr();
    halfdelay(1);
    keypad(stdscr, TRUE);
    if (has_colors()) {
        start_color();
        init_colorpairs();
        bkgd(COLOR_PAIR(COL_APP));
    } else {
        fprintf(stderr, "Non-colored terminals are not supported yet.");
        endwin();
        return EXIT_FAILURE;
    }
    atexit((void(*)())endwin);
    
    int exitcode;
    if (settings.singlemachine) {
        game_start_singlemachine(&settings);
        exitcode = EXIT_SUCCESS;
    } else if (settings.analyzepgn) {
        printw("Not implemented yet.\n");
        exitcode = EXIT_SUCCESS;
    } else {
        exitcode = is_server(&settings) ?
            server_run(&settings) : client_run(&settings);
    }
    
    mvaddstr(getmaxy(stdscr)-1, 0,
        "Game has ended. Press any key to leave...");
    clrtoeol();
    refresh();
    cbreak();
    flushinp();
    getch();
    
    return exitcode;
}

mercurial