home / pgnstats / playerstat.c

playerstat.c



//
//  playerstat.c
//  pgnstats
//
//  Created by anders on 03/08/2020.
//  Copyright © 2020 anders. All rights reserved.
//

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <float.h>
#include "playerstat.h"
#include "stringu.h"
#include "elo.h"

static PlayerStat players[MAX_PLAYERS];
static int player_count = 0;

static PlayerStat* add_profile(const char *name) {
    
    if (player_count >= MAX_PLAYERS - 1) {
        printf("too many players (> %d) \n", MAX_PLAYERS);
        exit(EXIT_FAILURE);
    }
    
    PlayerStat* player = &players[player_count];
    stringu_copy(player->name, name);
    player->index = player_count++;
    player->games = 0;
    player->wins = 0;
    player->losses = 0;
    player->draws = 0;
    player->score = 0;
    player->elo = START_ELO;
    for (int i = 0; i < MAX_PLAYERS; ++i) {
        player->opponent_score[i] = 0;
        player->opponent_games[i] = 0;
    }
    return player;
}

void playerstat_process(const Game* game) {
    PlayerStat* white_player = NULL;
    PlayerStat* black_player = NULL;
    
    for (int i = 0; i < player_count; ++i) {
        if (stringu_equals(game->White, players[i].name)) {
            white_player = &players[i];
        }
        if (stringu_equals(game->Black, players[i].name)) {
            black_player = &players[i];
        }
    }
    if (white_player == NULL) {
        white_player = add_profile(game->White);
    }
    if (black_player == NULL) {
        black_player = add_profile(game->Black);
    }
    
    white_player->games++;
    black_player->games++;
    white_player->opponent_games[black_player->index]++;
    black_player->opponent_games[white_player->index]++;
    

    int white_elo = white_player->elo;
    int black_elo = black_player->elo;
    
    if (game->Result == PGN_WHITE_WIN) {
        white_player->wins++;
        white_player->score += 1.0f;
        white_player->opponent_score[black_player->index] += 1.0f;
        black_player->losses++;
        white_player->elo = calculate_elo(white_elo, black_elo, 1.0f);
        black_player->elo = calculate_elo(black_elo, white_elo, 0.0f);
    }
    else if(game->Result == PGN_BLACK_WIN) {
        black_player->wins++;
        black_player->score += 1.0f;
        black_player->opponent_score[white_player->index] += 1.0f;
        white_player->losses++;
        white_player->elo = calculate_elo(white_elo, black_elo, 0.0f);
        black_player->elo = calculate_elo(black_elo, white_elo, 1.0f);
    }
    else if (game->Result == PGN_DRAW) {
        white_player->draws++;
        white_player->score += 0.5f;
        white_player->opponent_score[black_player->index] += 0.5f;
        black_player->draws++;
        black_player->score += 0.5f;
        black_player->opponent_score[white_player->index] += 0.5f;
        white_player->elo = calculate_elo(white_elo, black_elo, 0.5f);
        black_player->elo = calculate_elo(black_elo, white_elo, 0.5f);
    }
}

static void sort_players_by_score(PlayerStat **result, PlayerStat *list, int size) {
    
    bool c[size];
    for (int j = 0; j < size; ++j) c[j] = false;
    
    for (int j = 0; j < size; ++j) {
        int best_i = -1;
        float best = -1.0f;
        for (int i = 0; i < size; ++i) {
            if (c[i]) continue;
            
            float score = list[i].score;
            if (score > best) {
                best = score;
                best_i = i;
            }
        }
        c[best_i] = true;
        result[j] = &list[best_i];
    }
}

static int longest_player_name(PlayerStat *list, int size) {
    int longest = 1;
    for (int i = 0; i < size; ++i) {
        int next = (int) strlen(list[i].name);
        longest = next > longest ? next : longest;
    }
    return longest - 1;
}

void playerstat_print_summary(void) {
    printf("found %d players:\n", player_count);
    
    PlayerStat* sorted[MAX_PLAYERS];
    sort_players_by_score(sorted, players, player_count);
    
    int padding_names = longest_player_name(players, player_count) + 1;
    char placement_count[32];
    sprintf(placement_count, "%d.", player_count);
    int padding_placement = (int) strlen(placement_count);
    
    for (int i = 0; i < player_count; ++i) {
        PlayerStat* player = sorted[i];
        
        char placement[32];
        sprintf(placement, "%d.", i + 1);
        
        float percentage = player->score / (((float) player->games) / 100);
        printf("%-*s %-*s ", padding_placement, placement, padding_names, player->name);
        printf("%5.1f%%  ", percentage);
        printf("%.1f/%d  ", player->score, player->games);
        printf("(+%d -%d =%d)", player->wins, player->losses, player->draws);
        printf(" (%d)\n", player->elo);
//        printf("games %d\n", player->games);
//        printf("score %f\n\n", player->score);
    }
}

static void playerstat_print_individual_cross(int rank, const PlayerStat* player, PlayerStat** sorted) {
    printf("%d. %s\n", rank, player->name);
    
    int padding_names = longest_player_name(players, player_count) + 1;
    
    for (int i = 0; i < player_count; ++i) {
        PlayerStat* opponent = sorted[i];
        if (stringu_equals(player->name, opponent->name)) continue;
        int games = (int) player->opponent_games[opponent->index];
        if (games <= 0) continue;
        float score = player->opponent_score[opponent->index];
        float percentage = games <= 0 ? 0 : score / (((float) games) / 100);
        printf("    %-*s", padding_names, opponent->name);
        printf("%5.1f%%  ", percentage);
        printf("%.1f/%d\n", score, games);
    }
    
}

void playerstat_print_cross(void) {
    PlayerStat* sorted[MAX_PLAYERS];
    sort_players_by_score(sorted, players, player_count);
    
    for (int i = 0; i < player_count; ++i) {
        playerstat_print_individual_cross(i + 1, sorted[i], sorted);
    }
}