commit 02c05873bb9a00540c01b4753df50be15937445e
parent 21983141f1c8a2d43135c8e315a7feb20414065a
Author: Pablo Cárdenas <pablo.cardenas@imca.edu.pe>
Date: Sat, 22 Jun 2024 20:06:18 -0500
Update
Diffstat:
| M | Makefile | | | 2 | +- |
| M | fetch.sh | | | 10 | ++++++++-- |
| M | ttcli.c | | | 185 | ++++++++++++++++++++++++++++++++++++++++++++++--------------------------------- |
| M | ttsrv.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, ¤t);
@@ -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, ¤t);
- 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", "es);
- 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", "e);
- 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", "e);
+ 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;
}