tt

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

commit 02c05873bb9a00540c01b4753df50be15937445e
parent 21983141f1c8a2d43135c8e315a7feb20414065a
Author: Pablo Cárdenas <pablo.cardenas@imca.edu.pe>
Date:   Sat, 22 Jun 2024 20:06:18 -0500

Update

Diffstat:
MMakefile | 2+-
Mfetch.sh | 10++++++++--
Mttcli.c | 185++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
Mttsrv.c | 70+++++++++++++++++++++++++++++++++++++++-------------------------------
4 files changed, 157 insertions(+), 110 deletions(-)

diff --git a/Makefile b/Makefile @@ -1,4 +1,4 @@ -CFLAGS = -g +CFLAGS = -g -Wall -Wextra -Werror -Wpedantic LDFLAGS = `pkg-config --libs ncurses` `pkg-config --libs json-c` all: ttcli ttsrv diff --git a/fetch.sh b/fetch.sh @@ -1,4 +1,10 @@ #!/bin/sh -mkdir -p data -curl https://monkeytype.com/quotes/english.json -o data/quotes_english.json +if [ ! -f data/monkeytype/quotes_english.json ]; then + mkdir -p data/monkeytype + curl https://monkeytype.com/quotes/english.json \ + -o data/monkeytype/quotes_english.json + echo "Downloaded data/monkeytype/quotes_english.json" +else + echo "Existing data/monkeytype/quotes_english.json" +fi diff --git a/ttcli.c b/ttcli.c @@ -12,9 +12,9 @@ #define AHEAD_SHOW 4 #define AHEAD_HIDE 2 -#define WAIT_TIME 3000000000 -#define MAX(a, b) ((a) > (b) ? (a) : (b)) -#define MIN(a, b) ((a) > (b) ? (a) : (b)) +#define WAIT_TIME 2000000000 +#define MAX(a, b) ((a) < (b) ? (b) : (a)) +#define MIN(a, b) ((a) < (b) ? (a) : (b)) void backspace( int *const pos, @@ -41,10 +41,11 @@ void print_text( WINDOW *win, int pos, int error, - int count_errors, + int count_errors, + int count_slows, struct timespec last_back, struct timespec last_input, - struct timespec start_time, + struct timespec first_input, struct state *state_players, char num_players, char const *text, @@ -54,15 +55,14 @@ void print_text( short num_spaces) { wclear(win); - if (pos >= length) { - float elapsed = (last_input.tv_sec - start_time.tv_sec) + (last_input.tv_nsec - start_time.tv_nsec) / 1000000000.0; - - mvwprintw(win, 5, 24, "WPM: %.2f", (float) (length / 5) / (elapsed / 60)); - mvwprintw(win, 6, 24, "Errors: %2d", count_errors); - mvwprintw(win, 7, 15, "Waiting text from server..."); - wrefresh(win); - return; - } + float elapsed = + (last_input.tv_sec - first_input.tv_sec) + + (last_input.tv_nsec - first_input.tv_nsec) / 1000000000.0; + + mvwprintw(win, 9, 24, " WPM: %.0f",(pos / 5.0) / (elapsed / 60.0)); + mvwprintw(win, 10, 24, "Errors: %d", count_errors); + mvwprintw(win, 11, 24, " Slows: %d", count_slows); + wrefresh(win); struct timespec current; clock_gettime(CLOCK_REALTIME, &current); @@ -74,61 +74,83 @@ void print_text( unsigned short current_word = pos_to_word[pos]; - int space_start = current_word + AHEAD_HIDE < num_spaces - ? current_word + AHEAD_HIDE - : num_spaces - 1; - int space_end = current_word + AHEAD_SHOW < num_spaces - ? current_word + AHEAD_SHOW - : num_spaces - 1; - int start = spaces[space_start]; - int end = spaces[space_end]; + int space_start = MIN(current_word + AHEAD_HIDE, num_spaces - 1); + int space_end = MIN(current_word + AHEAD_SHOW, num_spaces - 1); wmove(win, 0, 0); - wattron(win, A_DIM); - for (int i = 0; i < MAX(spaces[current_word], 0); i++) { - wprintw(win, "%c", text[i]); - } - wattroff(win, A_DIM); - for (int i = MAX(spaces[current_word], 0); i < start; i++) { - if (error) { - wattron(win, COLOR_PAIR(2)); + for (int i = 0; i < length; i++) { + if (pos >= length) { + wprintw(win, "%c", text[i]); + } else if (i < spaces[current_word]) { + wattron(win, A_DIM); wprintw(win, "%c", text[i]); - wattroff(win, COLOR_PAIR(2)); - } else if (diff_back < WAIT_TIME) { + wattroff(win, A_DIM); + } else if (i < spaces[space_start]) { + if (error) { + wattron(win, COLOR_PAIR(2)); + wprintw(win, "%c", text[i]); + wattroff(win, COLOR_PAIR(2)); + } else if (diff_back < WAIT_TIME) { + wattron(win, A_BOLD); + wattron(win, COLOR_PAIR(1)); + wprintw(win, "%c", text[i]); + wattroff(win, COLOR_PAIR(1)); + wattroff(win, A_BOLD); + } else if (diff_input < WAIT_TIME) { + if (isalpha(text[i])) { + wprintw(win, " "); + } else { + wprintw(win, "%c", text[i]); + } + } else { + /* wattron(win, A_BOLD); */ + wprintw(win, "%c", text[i]); + /* wattroff(win, A_BOLD); */ + } + } else if (i < spaces[space_end]) { wattron(win, A_BOLD); - wattron(win, COLOR_PAIR(1)); wprintw(win, "%c", text[i]); - wattroff(win, COLOR_PAIR(1)); wattroff(win, A_BOLD); - } else if (diff_input < WAIT_TIME) { - wprintw(win, " "); } else { - /* wattron(win, A_BOLD); */ wprintw(win, "%c", text[i]); - /* wattroff(win, A_BOLD); */ } } - wattron(win, A_BOLD); - for (int i = start; i < end; i++) { - wprintw(win, "%c", text[i]); - } - wattroff(win, A_BOLD); - for (int i = end; i < length; i++) { - wprintw(win, "%c", text[i]); - } - for (int i = 0; i < num_players; i++) { - wattron(win, COLOR_PAIR(2)); - int state_pos = state_players[i].pos; - if (state_pos >= 0) { - mvwprintw( - win, - state_pos / 58, - state_pos % 58, - "%c", - text[state_pos]); + for (int i_player = 0; i_player < num_players; i_player++) { + int i = state_players[i_player].pos; + + int y = i / 58; + int x = i % 58; + + wattron(win, COLOR_PAIR(3)); + if (i < spaces[current_word]) { + wattron(win, A_DIM); + mvwprintw(win, y, x, "%c", text[i]); + wattroff(win, A_DIM); + } else if (i < spaces[space_start]) { + if (error) { + wattron(win, COLOR_PAIR(4)); + mvwprintw(win, y, x, "%c", text[i]); + wattroff(win, COLOR_PAIR(4)); + } else if (diff_back < WAIT_TIME) { + wattron(win, A_BOLD); + mvwprintw(win, y, x, "%c", text[i]); + wattroff(win, A_BOLD); + } else if (diff_input < WAIT_TIME) { + mvwprintw(win, y, x, "_"); + } else { + /* wattron(win, A_BOLD); */ + mvwprintw(win, y, x, "%c", text[i]); + /* wattroff(win, A_BOLD); */ + } + } else if (i < spaces[space_end]) { + wattron(win, A_BOLD); + mvwprintw(win, y, x, "%c", text[i]); + wattroff(win, A_BOLD); + } else { + mvwprintw(win, y, x, "%c", text[i]); } - wattroff(win, COLOR_PAIR(2)); + wattroff(win, COLOR_PAIR(3)); } if (error) { @@ -174,7 +196,7 @@ int main(int argc, const char *argv[]) if (status == -1) { perror("connect"); endwin(); - exit(1); + return 1; } initscr(); @@ -183,6 +205,8 @@ int main(int argc, const char *argv[]) noecho(); init_pair(1, COLOR_RED, -1); init_pair(2, -1, COLOR_RED); + init_pair(3, COLOR_CYAN, -1); + init_pair(4, COLOR_CYAN, COLOR_RED); WINDOW *boxwin = newwin(15, 60, (LINES - 15) / 2, (COLS - 60) / 2); WINDOW *win = subwin( @@ -193,7 +217,6 @@ int main(int argc, const char *argv[]) refresh(); wrefresh(boxwin); - struct state state_players[16]; char num_players = 0; @@ -208,6 +231,7 @@ int main(int argc, const char *argv[]) int pos; int error; int count_errors = 0; + int count_slows = 0; char text[4096]; int length; @@ -218,11 +242,7 @@ int main(int argc, const char *argv[]) struct timespec last_back = {0, 0}; struct timespec last_input = {0, 0}; - struct timespec start_time; - - int count_read_stdin = 0; - int count_read_sockfd = 0; - int count_write_sockfd = 0; + struct timespec first_input = {0, 0}; while (1) { fd_set readfds, writefds; @@ -238,6 +258,10 @@ int main(int argc, const char *argv[]) select(FD_SETSIZE, &readfds, &writefds, NULL, &select_timeout); if (FD_ISSET(0, &readfds)) { + if (first_input.tv_sec == 0 && first_input.tv_nsec == 0) { + clock_gettime(CLOCK_REALTIME, &first_input); + } + int c = getch(); if (c == ERR) { @@ -265,16 +289,21 @@ int main(int argc, const char *argv[]) } else { struct timespec current; clock_gettime(CLOCK_REALTIME, &current); - long diff = + long diff_back = (current.tv_sec - last_back.tv_sec) * 1000000000 + current.tv_nsec - last_back.tv_nsec; + long diff_input = + (current.tv_sec - last_input.tv_sec) * + 1000000000 + + current.tv_nsec - last_input.tv_nsec; - if (!error && diff > WAIT_TIME) { + if (pos < length && !error && diff_back > WAIT_TIME) { if (c == text[pos]) { - clock_gettime( - CLOCK_REALTIME, - &last_input); + if (pos != 0 && diff_input > WAIT_TIME) { + count_slows++; + } + last_input = current; pos++; outdated = 1; } else { @@ -289,10 +318,11 @@ int main(int argc, const char *argv[]) if (recv_step == 0) { memset(&last_back, 0, sizeof last_back); memset(&last_input, 0, sizeof last_input); - clock_gettime(CLOCK_REALTIME, &start_time); + memset(&first_input, 0, sizeof first_input); error = 0; count_errors = 0; + count_slows = 0; length = recv(sockfd, text, 4096, 0); if (length == 0) { @@ -317,7 +347,8 @@ int main(int argc, const char *argv[]) pos = 0; } else if (recv_step == 1) { - int recv_length = recv(sockfd, &num_players, 1, 0); + int recv_length = + recv(sockfd, &num_players, 1, 0); if (recv_length == 0) { endwin(); fprintf(stderr, "Closed by server\n"); @@ -330,10 +361,11 @@ int main(int argc, const char *argv[]) recv_step = 0; } } else if (recv_step == 2) { - int recv_length = recv(sockfd, - state_players, - num_players * sizeof(struct state), - 0); + int recv_length = + recv(sockfd, + state_players, + num_players * sizeof(struct state), + 0); if (recv_length == 0) { endwin(); fprintf(stderr, "Closed by server\n"); @@ -358,9 +390,10 @@ int main(int argc, const char *argv[]) pos, error, count_errors, + count_slows, last_back, last_input, - start_time, + first_input, state_players, num_players, text, diff --git a/ttsrv.c b/ttsrv.c @@ -1,34 +1,35 @@ -#include <sys/socket.h> +#include "tt.h" +#include <json-c/json.h> #include <netinet/in.h> #include <stdio.h> -#include <unistd.h> -#include "tt.h" #include <stdlib.h> -#include <json-c/json.h> #include <string.h> +#include <sys/socket.h> +#include <unistd.h> -int main(int argc, const char* argv[]) { - if (argc != 2) { - fprintf(stderr, "Usage: %s <port>\n", argv[0]); +int main(int argc, const char *argv[]) +{ + if (argc != 3) { + fprintf(stderr, "Usage: %s <port> <quotes.json>\n", argv[0]); return 1; } - char* endptr; + char *endptr; long port = strtol(argv[1], &endptr, 10); if (!(*argv[1] != '\0' && *endptr == '\0' && port < 65536)) { fprintf(stderr, "ERROR: '%s' is not a valid port.\n", argv[1]); return 1; } - struct json_object *root = json_object_from_file("data/quotes_english.json"); + struct json_object *root = + json_object_from_file(argv[2]); if (!root) { fprintf(stderr, "Failed to parse JSON file.\n"); return 1; } struct json_object *quotes; json_object_object_get_ex(root, "quotes", &quotes); - size_t num_quotes = json_object_array_length(quotes); - + int num_quotes = json_object_array_length(quotes); int sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd == -1) { @@ -39,9 +40,8 @@ int main(int argc, const char* argv[]) { struct sockaddr_in address = { .sin_family = AF_INET, .sin_port = htons(port), - .sin_addr.s_addr = INADDR_ANY - }; - if (bind(sockfd, (struct sockaddr*) &address, sizeof address) == -1){ + .sin_addr.s_addr = INADDR_ANY}; + if (bind(sockfd, (struct sockaddr *)&address, sizeof address) == -1) { perror("bind"); return 1; } @@ -56,11 +56,8 @@ int main(int argc, const char* argv[]) { FD_SET(sockfd, &set); struct state states[FD_SETSIZE]; + int length = 0; - char running = 0; - - printf("\nChoose quote index: "); - fflush(stdout); while (1) { fd_set readfds = set; if (select(FD_SETSIZE, &readfds, NULL, NULL, NULL) == -1) { @@ -72,38 +69,46 @@ int main(int argc, const char* argv[]) { char buffer[4096]; read(0, buffer, 4096); long quote_idx = strtol(buffer, &endptr, 10); - if (!((*buffer != '\n' && *buffer != '\0') && (*endptr == '\0' || *endptr =='\n'))) { + if (!((*buffer != '\n' && *buffer != '\0') && + (*endptr == '\0' || *endptr == '\n'))) { fprintf(stderr, "Error parsing quote index.\n"); + } else if (!(0 <= quote_idx && quote_idx < num_quotes)) { + fprintf(stderr, "0 <= quote_index < %d.\n", num_quotes); } else { - json_object *quote = json_object_array_get_idx(quotes, quote_idx); - json_object_object_get_ex(quote, "text", &quote); - const char *str_quote = json_object_get_string(quote); + json_object *quote = json_object_array_get_idx( + quotes, quote_idx); + json_object_object_get_ex( + quote, "text", &quote); + const char *str_quote = + json_object_get_string(quote); + length = strlen(str_quote); for (int i = 0; i < FD_SETSIZE; i++) { states[i].pos = 0; - if (!(i != 0 && i != sockfd && FD_ISSET(i, &set))) { + if (!(i != 0 && i != sockfd && + FD_ISSET(i, &set))) { continue; } - send(i, str_quote, strlen(str_quote), 0); + send(i, str_quote, length, 0); } } - printf("\nChoose quote index: "); - fflush(stdout); - } if (FD_ISSET(sockfd, &readfds)) { int fd = accept(sockfd, NULL, NULL); FD_SET(fd, &set); + states[fd].pos = 0; printf("player %d connected\n", fd); } for (int fd = 0; fd < FD_SETSIZE; fd++) { - if (!(fd != 0 && fd != sockfd && FD_ISSET(fd, &readfds))) { + if (!(fd != 0 && fd != sockfd && + FD_ISSET(fd, &readfds))) { continue; } int length = recv(fd, &states[fd], 16, 0); if (length == 0) { + states[fd].pos = 0; printf("Disconnected\n"); FD_CLR(fd, &set); continue; @@ -112,15 +117,18 @@ int main(int argc, const char* argv[]) { struct state buffer[FD_SETSIZE]; unsigned short buffer_size = 0; for (int i = 0; i < FD_SETSIZE; i++) { - if (!(i != 0 && i != fd && i != sockfd && FD_ISSET(i, &set))) { + if (!(i != 0 && i != fd && i != sockfd && + FD_ISSET(i, &set))) { continue; } buffer[buffer_size++] = states[i]; } send(fd, &buffer_size, 1, 0); - send(fd, buffer, buffer_size * (sizeof (struct state)), 0); + send(fd, + buffer, + buffer_size * (sizeof(struct state)), + 0); } - } return 0; }