From 831f9f01fbe4088eb6bd378c0e417d9996b676fd Mon Sep 17 00:00:00 2001 From: scratko Date: Fri, 30 Aug 2024 12:46:56 +0300 Subject: Final version v2.0 Added windows client. SIGPIPE signal was being sent to the server when the client was disconnected. Now there is handling of this signal. Added a delay when displaying some informational messages. --- client/Makefile | 23 -- client/card_handling.c | 64 ----- client/card_handling.h | 9 - client/card_stack.c | 173 ------------ client/card_stack.h | 30 -- client/client.c | 322 --------------------- client/client.h | 84 ------ client/data_decryption.c | 181 ------------ client/data_decryption.h | 16 -- client/printing_game_frames.c | 183 ------------ client/printing_game_frames.h | 20 -- client/verification_client_input.c | 115 -------- client/verification_client_input.h | 12 - linux_client/Makefile | 23 ++ linux_client/card_handling.c | 64 +++++ linux_client/card_handling.h | 9 + linux_client/card_stack.c | 173 ++++++++++++ linux_client/card_stack.h | 30 ++ linux_client/client.c | 321 +++++++++++++++++++++ linux_client/client.h | 84 ++++++ linux_client/data_decryption.c | 181 ++++++++++++ linux_client/data_decryption.h | 16 ++ linux_client/printing_game_frames.c | 183 ++++++++++++ linux_client/printing_game_frames.h | 20 ++ linux_client/verification_client_input.c | 115 ++++++++ linux_client/verification_client_input.h | 12 + server/server.c | 8 + server/server_data_processing.c | 24 +- windows_client/Makefile | 22 ++ windows_client/card_handling.c | 64 +++++ windows_client/card_handling.h | 9 + windows_client/card_stack.c | 173 ++++++++++++ windows_client/card_stack.h | 30 ++ windows_client/client.c | 440 +++++++++++++++++++++++++++++ windows_client/client.h | 87 ++++++ windows_client/data_decryption.c | 181 ++++++++++++ windows_client/data_decryption.h | 16 ++ windows_client/printing_game_frames.c | 183 ++++++++++++ windows_client/printing_game_frames.h | 20 ++ windows_client/verification_client_input.c | 116 ++++++++ windows_client/verification_client_input.h | 12 + 41 files changed, 2614 insertions(+), 1234 deletions(-) delete mode 100644 client/Makefile delete mode 100644 client/card_handling.c delete mode 100644 client/card_handling.h delete mode 100644 client/card_stack.c delete mode 100644 client/card_stack.h delete mode 100644 client/client.c delete mode 100644 client/client.h delete mode 100644 client/data_decryption.c delete mode 100644 client/data_decryption.h delete mode 100644 client/printing_game_frames.c delete mode 100644 client/printing_game_frames.h delete mode 100644 client/verification_client_input.c delete mode 100644 client/verification_client_input.h create mode 100644 linux_client/Makefile create mode 100644 linux_client/card_handling.c create mode 100644 linux_client/card_handling.h create mode 100644 linux_client/card_stack.c create mode 100644 linux_client/card_stack.h create mode 100644 linux_client/client.c create mode 100644 linux_client/client.h create mode 100644 linux_client/data_decryption.c create mode 100644 linux_client/data_decryption.h create mode 100644 linux_client/printing_game_frames.c create mode 100644 linux_client/printing_game_frames.h create mode 100644 linux_client/verification_client_input.c create mode 100644 linux_client/verification_client_input.h create mode 100644 windows_client/Makefile create mode 100644 windows_client/card_handling.c create mode 100644 windows_client/card_handling.h create mode 100644 windows_client/card_stack.c create mode 100644 windows_client/card_stack.h create mode 100644 windows_client/client.c create mode 100644 windows_client/client.h create mode 100644 windows_client/data_decryption.c create mode 100644 windows_client/data_decryption.h create mode 100644 windows_client/printing_game_frames.c create mode 100644 windows_client/printing_game_frames.h create mode 100644 windows_client/verification_client_input.c create mode 100644 windows_client/verification_client_input.h diff --git a/client/Makefile b/client/Makefile deleted file mode 100644 index 2e2ce88..0000000 --- a/client/Makefile +++ /dev/null @@ -1,23 +0,0 @@ -SRCMODULES = card_handling.c card_stack.c data_decryption.c\ - printing_game_frames.c verification_client_input.c client.c -OBJMODULES = $(SRCMODULES:.c=.o) -CC = gcc -CFLAGS = -Wall -g -c - -all: client - -%.o: %.с %.h - $(CC) $(CFLAGS) $< -o $@ - -client: $(OBJMODULES) - $(CC) $(LIBS) $^ -o $@ - -ifneq (clean, $(MAKECMDGOALS)) --include deps.mk -endif - -deps.mk: $(SRCMODULES) - $(CC) -MM $^ > $@ - -clean: - rm -f *.o client diff --git a/client/card_handling.c b/client/card_handling.c deleted file mode 100644 index 011ad40..0000000 --- a/client/card_handling.c +++ /dev/null @@ -1,64 +0,0 @@ -#include "card_handling.h" - -#include -#include - -int convert_rank_to_int(const char *card) -{ - int length; - char str_rank[2]; - - length = strlen(card); - /* 10 - the only one of its kind */ - if(length == 3) - return 10; - - str_rank[0] = card[0]; - str_rank[1] = '\0'; - - switch(card[0]) { - case 'J': - return 11; - case 'Q': - return 12; - case 'K': - return 13; - case 'A': - return 14; - default: - return strtol(str_rank, NULL, 10); - } - return 0; -} - -int is_card_beaten(const char *attack_card, const char *defend_card, - const char *trump_suit) -{ - int length, attack_rank, defend_rank; - const char *attack_suit, *defend_suit; - - length = strlen(attack_card); - attack_suit= attack_card + length - 1; - length = strlen(defend_card); - defend_suit = defend_card + length - 1; - - /* suits matched */ - if(!strcmp(attack_suit, defend_suit)) { - attack_rank = convert_rank_to_int(attack_card); - defend_rank = convert_rank_to_int(defend_card); - if(defend_rank > attack_rank) - return 1; - /* defender has a trump suit */ - } else if(!strcmp(defend_suit, trump_suit)) - return 1; - return 0; -} - -int check_matched_ranks(const char *attack_card, const char *not_defender_card) -{ - int attack_rank, not_defender_rank; - - attack_rank = convert_rank_to_int(attack_card); - not_defender_rank = convert_rank_to_int(not_defender_card); - return attack_rank == not_defender_rank; -} diff --git a/client/card_handling.h b/client/card_handling.h deleted file mode 100644 index 8dcb072..0000000 --- a/client/card_handling.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef CARD_HANDLING_H_SENTRY -#define CARD_HANDLING_H_SENTRY - -int convert_rank_to_int(const char *card); -int is_card_beaten(const char *attack_card, const char *defend_card, - const char *trump_suit); -int check_matched_ranks(const char *attack_card, const char *not_defender_card); - -#endif diff --git a/client/card_stack.c b/client/card_stack.c deleted file mode 100644 index fc4057c..0000000 --- a/client/card_stack.c +++ /dev/null @@ -1,173 +0,0 @@ -#include "card_stack.h" -#include "card_handling.h" -#include "client.h" - -#include -#include - -void init_stack(player_cards *deck) -{ - *deck = NULL; -} - -void push_stack(player_cards *deck, char *str) -{ - struct card_stack_item *tmp = malloc(sizeof(struct card_stack_item)); - tmp->str = str; - tmp->tip = ' '; - tmp->is_usable = 0; - tmp->next = *deck; - *deck = tmp; -} - -void clear_stack(player_cards *deck) -{ - struct card_stack_item *tmp; - - while(*deck) { - tmp = *deck; - *deck = tmp->next; - if(tmp->str) - free(tmp->str); - free(tmp); - } -} - -int is_empty_stack(player_cards deck) -{ - return deck == NULL; -} - -void add_hint_letters_stack(player_cards deck) -{ - unsigned char letter = 'a'; - - while(deck) { - deck->tip = letter; - ++letter; - if(letter > 'z') - letter = 'A'; - deck = deck->next; - } -} - -static int check_no_attackers_cards_marked(player_cards deck) -{ - while(deck) { - if(deck->is_usable) - return 0; - deck = deck->next; - } - return 1; -} - -static void mark_all_card_stack(player_cards deck) -{ - player_cards tmp_deck = deck; - - while(tmp_deck) { - tmp_deck->is_usable = 1; - tmp_deck = tmp_deck->next; - } -} - -static void clear_is_usable_status(player_cards deck) -{ - while(deck) { - if(deck->is_usable) - deck->is_usable = 0; - deck = deck->next; - } -} - -void mark_card_for_attackers_stack(player_cards deck) -{ - char *card = NULL, *found_card; - struct card_stack_item *search_deck = NULL; - int target_rank, found_rank; - player_cards tmp_begin_deck = deck; - - clear_is_usable_status(deck); - while(deck) { - /* is the card already marked? */ - if(deck->is_usable) { - deck = deck->next; - continue; - } - card = deck->str; - target_rank = convert_rank_to_int(card); - search_deck = deck->next; - while(search_deck) { - if(search_deck->is_usable) { - search_deck = search_deck->next; - continue; - } - found_card = search_deck->str; - found_rank = convert_rank_to_int(found_card); - if(found_rank == target_rank) { - deck->is_usable = 1; - search_deck->is_usable = 1; - } - search_deck = search_deck->next; - } - deck = deck->next; - } - if(check_no_attackers_cards_marked(tmp_begin_deck)) - mark_all_card_stack(tmp_begin_deck); -} - -void mark_card_for_defenders_stack(player_cards deck, - struct cards_on_table *cot, char *trump_suit) -{ - int i; - - while(deck) { - for(i = 0; i <= cot->card_arr_idx; ++i) - if(((i == 0 || !(i % 3)) && cot->card_arr[i+2][0] == '-') && - is_card_beaten(cot->card_arr[i], deck->str, trump_suit)) - { - deck->is_usable = 1; - break; - } - deck = deck->next; - } -} - -void mark_card_for_tossing_stack(player_cards deck, struct cards_on_table *cot) -{ - int i; - - while(deck) { - for(i = 0; i <= cot->card_arr_idx; ++i) { - if(cot->card_arr[i][0] == '-' || cot->card_arr[i][0] == '\\') - continue; - if(check_matched_ranks(cot->card_arr[i], deck->str)) { - deck->is_usable = 1; - break; - } - } - deck = deck->next; - } -} - -char* card_search_by_marked_letter(player_cards deck, int letter) -{ - while(deck) { - if(deck->tip == letter && deck->is_usable) { - deck->is_usable = 0; - return deck->str; - } - deck = deck->next; - } - return NULL; -} - -char* card_search_by_unmarked_letter(player_cards deck, int letter) -{ - while(deck) { - if(deck->tip == letter) - return deck->str; - deck = deck->next; - } - return NULL; -} diff --git a/client/card_stack.h b/client/card_stack.h deleted file mode 100644 index 0daf40a..0000000 --- a/client/card_stack.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef CARD_STACK_H_SENTRY -#define CARD_STACK_H_SENTRY - -struct card_stack_item { - char *str; - struct card_stack_item *next; - /* like a, b, c etc */ - unsigned char tip; - /* 1 - can be used, 0 - can't take it */ - int is_usable; -}; - -typedef struct card_stack_item* player_cards; - -void init_stack(player_cards *deck); -void push_stack(player_cards *deck, char *str); -void clear_stack(player_cards *deck); -int is_empty_stack(player_cards deck); -void add_hint_letters_stack(player_cards deck); -void mark_card_for_attackers_stack(player_cards deck); - -struct cards_on_table; -void mark_card_for_defenders_stack(player_cards deck, - struct cards_on_table *cot, - char* trump_suit); -void mark_card_for_tossing_stack(player_cards deck, struct cards_on_table *cot); -char* card_search_by_marked_letter(player_cards deck, int letter); -char* card_search_by_unmarked_letter(player_cards deck, int letter); - -#endif diff --git a/client/client.c b/client/client.c deleted file mode 100644 index 20279d8..0000000 --- a/client/client.c +++ /dev/null @@ -1,322 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include /* for debug */ - -#include "client.h" -#include "data_decryption.h" -#include "printing_game_frames.h" -#include "verification_client_input.h" - -static const char *ip = "127.0.0.1"; -static const int port = 1025; - -static void clean_up_resources(struct client *cl) -{ - free(cl->cc.number_arr); - clear_stack(&cl->deck); -} - -static void get_data_from_server(struct client *cl, fd_set *readfds) -{ - int i; - int update_info = 0; - /* pointer to delimeter (:) that is behind extract data */ - char *end_p; - - if(FD_ISSET(cl->fd, readfds)) { - if(!cl->data_left_in_buffer) { - cl->data_left_in_buffer = read(cl->fd, cl->buffer, max_buffer_size); - /* end of file -- closed connection (from server) */ - if(!cl->data_left_in_buffer) { - clean_up_resources(cl); - exit(0); - } -#ifdef DEBUG - printf("%d\n", cl->data_left_in_buffer); - for(i = 0; i < cl->data_left_in_buffer; ++i) - putchar(cl->buffer[i]); - putchar('\n'); -#endif - } - decrypt_set_state(cl, &end_p); - update_info = 1; - cl->display_new_frame = 1; - cl->pending_server_response = 0; - } - if(update_info) { - switch(cl->state) { - case first_player: - break; - case confirmation_waiting: - cl->total_players = decrypt_get_number(end_p+1, &end_p); - break; - case display_only_table: - case tossing_expectation: - case tossing: - /* last arg will contain pointer to '\n' */ - decrypt_set_base_info(cl, end_p+1, &end_p); - break; - case attack: - case defense: - case attack_expectation: - decrypt_set_base_info(cl, end_p+1, &end_p); - decrypt_set_position_whose_turn(cl, end_p+1, &end_p); - break; - case defense_expectation: - case spectator: - decrypt_set_base_info(cl, end_p+1, &end_p); - decrypt_set_position_whose_turn(cl, end_p+1, &end_p); - decrypt_set_card_queue(cl, end_p+1, &end_p); - if(cl->state == spectator) - decrypt_set_spectator_mode(cl, end_p+1); - break; - case card_acceptance_status: - decrypt_set_card_acceptance_status(cl, end_p+1); - break; - case result: - decrypt_set_durak_position(cl, end_p+1); - break; - /* no data to extract */ - case tossing_limit_status: - case disconnect: - default: - {} - } - for(i = 0; cl->buffer[i] != '\n'; ++i) - {} - if((cl->buffer+i - cl->buffer + 1) != cl->data_left_in_buffer) { - cl->data_left_in_buffer -= i + 1; - memmove(cl->buffer, cl->buffer+i+1, cl->data_left_in_buffer); - } else - cl->data_left_in_buffer = 0; - } -} - -static void prepare_tips_for_client(struct client *cl) -{ - char *trump_suit = NULL; - - if(!is_empty_stack(cl->deck)) - add_hint_letters_stack(cl->deck); -/* TODO: clear marked cards ??? */ - switch(cl->state) { - case attack: - mark_card_for_attackers_stack(cl->deck); - break; - case defense: - trump_suit = cl->trump_card + strlen(cl->trump_card) - 1; - mark_card_for_defenders_stack(cl->deck, &cl->cot, trump_suit); - break; - case tossing: - mark_card_for_tossing_stack(cl->deck, &cl->cot); - break; - default: - {} - } -} - -static void change_client_frame(struct client *cl) -{ - if(cl->display_new_frame) { - pgf_new_frame(); - switch(cl->state) { - case first_player: - pgf_first_player(); - break; - case confirmation_waiting: - pgf_confirmation_waiting(cl->total_players); - break; - case display_only_table: - case attack_expectation: - case defense_expectation: - case tossing_expectation: - pgf_table(cl); - if(cl->state == attack_expectation || - cl->state == defense_expectation || - cl->state == tossing_expectation) - { - pgf_select_idle_mode_message(cl->state); - } - break; - case spectator: - pgf_table(cl); - pgf_spectator_mode(cl->sp_mode); - break; - case attack: - case defense: - case tossing: - pgf_table(cl); - pgf_suggestions(cl); - break; - case card_acceptance_status: - pgf_card_acceptance_status(cl->all_input_cards_accepted); - break; - case tossing_limit_status: - pgf_tossing_limit_status(); - break; - case result: - pgf_game_result(cl->durak_position); - break; - case disconnect: - pgf_disconnect(); - break; - case none: - {} - } - cl->display_new_frame = 0; - } -} - -static int check_users_exit(enum client_states state, const char *buffer, - int size) -{ - if((state == first_player || state == confirmation_waiting) && - size == 2 && (buffer[0] == 'q' && buffer[1] == '\n')) - return 1; - - return size == 3 && !strncmp(buffer, "qq", 2) && buffer[2] == '\n'; -} - -static void send_data_to_server(struct client *cl, fd_set *readfds) -{ - int input_result = 0, data_size; - - if(FD_ISSET(0, readfds)) { /* 0 - stdin */ - data_size = read(0, cl->buffer, max_buffer_size); - if(check_users_exit(cl->state, cl->buffer, data_size)) { - close(cl->fd); - clean_up_resources(cl); - pgf_disconnect(); - exit(0); - } - - switch(cl->state) { - case confirmation_waiting: - input_result = vci_confirmation_waiting(cl->fd, cl->buffer); - break; - case attack: - case tossing: - input_result = vci_attack_or_tossing(cl->fd, cl->buffer, cl->deck, - cl->state); - break; - case defense: - input_result = vci_defense(cl->fd, cl->buffer, cl->deck); - break; - default: - {} - } - /* if 0, then re-tracking the input client */ - if(input_result) - cl->pending_server_response = 1; - } -} - -static int check_tracking_client_input(enum client_states state, - int pending_server_response) -{ - if(pending_server_response) - return 0; - - switch(state) { - case first_player: - case confirmation_waiting: - case attack: - case defense: - case tossing: - return 1; - default: - return 0; - } - return 0; -} - -int main_loop(struct client *cl) -{ - int select_result; - fd_set readfds; - - for(;;) { - FD_ZERO(&readfds); - - FD_SET(cl->fd, &readfds); - if(check_tracking_client_input(cl->state, cl->pending_server_response)) - FD_SET(0, &readfds); - - if(!cl->data_left_in_buffer) { - select_result = select(cl->fd + 1, &readfds, NULL, NULL, NULL); - if(select_result == -1) - return 2; - } - get_data_from_server(cl, &readfds); - prepare_tips_for_client(cl); - if(cl->display_new_frame) - change_client_frame(cl); - send_data_to_server(cl, &readfds); - } -} - -static void init_client(struct client *cl) -{ - cl->fd = -1; - cl->state = none; - cl->total_players = 0; - cl->total_cards_left = 0; - cl->player_position = 0; - cl->cc.number_arr = NULL; - cl->cc.number_arr_idx = -1; - cl->cot.card_arr_idx = -1; - cl->deck = NULL; - cl->pending_server_response = 0; - cl->cq.card_arr_idx = -1; - cl->position_whose_turn = 0; - cl->display_new_frame = 0; - cl->all_input_cards_accepted = 0; - cl->data_left_in_buffer = 0; - cl->sp_mode = spectator_table; - cl->durak_position = 0; -} - -/* - * 0 - failure - * 1 - success - */ -static int connect_to_server(struct client *cl) -{ - int connect_result; - struct sockaddr_in addr; - - cl->fd = socket(AF_INET, SOCK_STREAM, 0); - - if(cl->fd == -1) - return 0; - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = inet_addr(ip); - addr.sin_port = htons(port); - - connect_result = connect(cl->fd, (struct sockaddr*) &addr, sizeof(addr)); - if(connect_result == -1) - return 0; - return 1; -} - -int main() -{ - struct client cl; - - pgf_new_frame(); - pgf_welcome(); - sleep(2); - init_client(&cl); - if(!connect_to_server(&cl)) { - pgf_new_frame(); - pgf_connection(0); - return 1; - } - pgf_new_frame(); - pgf_connection(1); - return main_loop(&cl); -} diff --git a/client/client.h b/client/client.h deleted file mode 100644 index 12ec619..0000000 --- a/client/client.h +++ /dev/null @@ -1,84 +0,0 @@ -#ifndef CLIENT_H_SENTRY -#define CLIENT_H_SENTRY - -#include "card_stack.h" - -enum { - max_buffer_size = 4096, - max_cot_arr_size = 18, - card_size = 4, - max_cq_arr_size = 5 -}; - -enum client_states { - none, - first_player, - confirmation_waiting, - /* in game */ - display_only_table, - attack_expectation, - defense_expectation, - tossing_expectation, - attack, - defense, - tossing, - card_acceptance_status, - tossing_limit_status, - spectator, - result, - disconnect -}; - -enum spectator_mode { - spectator_attack, - spectator_defense, - spectator_tossing, - spectator_table -}; - -struct cards_on_table { - /* - * example: 2^ \ - 10# \ K# - * [2 ^ '\0' ][1 0 # '\0'][\ '\0' ] - */ - char card_arr[max_cot_arr_size][card_size]; - /* index of the last filled element */ - int card_arr_idx; -}; - -struct card_queue { - char card_arr[max_cq_arr_size][card_size]; - int card_arr_idx; -}; - -struct card_count { - int *number_arr; - /* - * will only store the initial number of players - * (idx: total_players - 1 - */ - int number_arr_idx; -}; - -struct client { - int fd; - enum client_states state; - char buffer[max_buffer_size]; - int total_players; - int total_cards_left; - int player_position; - char trump_card[4]; - struct card_count cc; - struct cards_on_table cot; - player_cards deck; - int pending_server_response; - struct card_queue cq; - int position_whose_turn; - int display_new_frame; - int all_input_cards_accepted; - int data_left_in_buffer; - enum spectator_mode sp_mode; - int durak_position; -}; - -#endif diff --git a/client/data_decryption.c b/client/data_decryption.c deleted file mode 100644 index 1054c45..0000000 --- a/client/data_decryption.c +++ /dev/null @@ -1,181 +0,0 @@ -#include "data_decryption.h" -#include "client.h" -#include "card_stack.h" - -#include -#include -#include - -void decrypt_set_state(struct client *cl, char **end_p) -{ - enum client_states found_state; - - found_state = (enum client_states) strtol(cl->buffer, end_p, 10); - cl->state = found_state; -} - -/* return convert number from str */ -int decrypt_get_number(char *start_p, char **end_p) -{ - return strtol(start_p, end_p, 10); -} - -static void decrypt_set_trump_card(struct client *cl, char *start_p, - char **end_p) -{ - int i; - - for(i = 0; *start_p != ':'; ++start_p, ++i) - cl->trump_card[i] = *start_p; - cl->trump_card[i] = '\0'; - *end_p = start_p; -} - -void decrypt_set_base_info(struct client *cl, char *start_p, char **end_p) -{ - int i, j; - char tmp_card[4]; - char *card = NULL; - - cl->total_players = decrypt_get_number(start_p, end_p); - cl->total_cards_left = decrypt_get_number(*end_p+1, end_p); - cl->player_position = decrypt_get_number(*end_p+1, end_p); - decrypt_set_trump_card(cl, *end_p+1, end_p); - - if(!cl->cc.number_arr) { - cl->cc.number_arr = malloc(cl->total_players * sizeof(int)); - /* - * memorize the size of the array, - * because later the number of players may change (client disconnection) - */ - cl->cc.number_arr_idx = cl->total_players-1; - } - - /* ============== extraction quantity of cards =============== */ - - /* numbers are separated by '\' */ - i = 0; - do { - cl->cc.number_arr[i] = decrypt_get_number(*end_p+1, end_p); - ++i; - } while(**end_p != ':'); - - /* ============== extraction cards on table =============== */ - i = 0, j = 0; - for(start_p = *end_p + 1; *start_p != ':'; ++start_p) { - /* empty table */ - if(*start_p == '=') { - /* to delimiter : */ - ++start_p; - i = -1; - break; - } - /* delimiter between cards on table */ - if(*start_p == '+') { - cl->cot.card_arr[i][j] = '\0'; - ++i; - j = 0; - continue; - } - cl->cot.card_arr[i][j] = *start_p; - ++j; - } - /* for the last card we need to add a null character */ - if(i != -1) - cl->cot.card_arr[i][j] = '\0'; - cl->cot.card_arr_idx = i; - - /* ================= extraction player's deck ================*/ - /* clear previous stack */ - if(cl->deck) - clear_stack(&cl->deck); - init_stack(&cl->deck); - - for(++start_p, i = 0; *start_p != ':' && *start_p != '\n'; ++start_p) { - /* empty deck */ - if(*start_p == '=') { - i = -1; - /* to delimiter ':' or '\n' */ - ++start_p; - break; - } - /* delimiter */ - if(*start_p == '\\') { - tmp_card[i] = '\0'; - card = malloc(strlen(tmp_card) + 1); - strcpy(card, tmp_card); - push_stack(&cl->deck, card); - i = 0; - continue; - } - tmp_card[i] = *start_p; - ++i; - } - if(i != -1) { - tmp_card[i] = '\0'; - card = malloc(strlen(tmp_card) + 1); - strcpy(card, tmp_card); - push_stack(&cl->deck, card); - } - *end_p = start_p; -} - -void decrypt_set_position_whose_turn(struct client *cl, char *start_p, - char **end_p) -{ - cl->position_whose_turn = decrypt_get_number(start_p, end_p); -} - -void decrypt_set_card_queue(struct client *cl, char *start_p, char **end_p) -{ - int i, j; - - for(i = 0, j = 0; *start_p != ':' && *start_p != '\n'; ++start_p) { - /* empty queue */ - if(*start_p == '=') { - i = -1; - ++start_p; - break; - } - /* delimiter '\' */ - if(*start_p == '\\') { - cl->cq.card_arr[i][j] = '\0'; - j = 0; - ++i; - continue; - } - cl->cq.card_arr[i][j] = *start_p; - ++j; - } - /* last element */ - if(i != -1) - cl->cq.card_arr[i][j] = '\0'; - cl->cq.card_arr_idx = i; - *end_p = start_p; -} - -void decrypt_set_card_acceptance_status(struct client *cl, char *start_p) -{ - int i; - char tmp_buffer[8]; - - for(i = 0; *start_p != '\n'; ++start_p, ++i) - tmp_buffer[i] = *start_p; - tmp_buffer[i] = '\0'; - if(!strcmp(tmp_buffer, "all")) { - cl->all_input_cards_accepted = 1; - return; - } - if(!strcmp(tmp_buffer, "not all")) - cl->all_input_cards_accepted = 0; -} - -void decrypt_set_spectator_mode(struct client *cl, char *start_p) -{ - cl->sp_mode = (enum spectator_mode) decrypt_get_number(start_p, NULL); -} - -void decrypt_set_durak_position(struct client *cl, char *start_p) -{ - cl->durak_position = decrypt_get_number(start_p, NULL); -} diff --git a/client/data_decryption.h b/client/data_decryption.h deleted file mode 100644 index 08772fe..0000000 --- a/client/data_decryption.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef DATA_DECRYPTION_H_SENTRY -#define DATA_DECRYPTION_H_SENTRY - -#include "client.h" - -void decrypt_set_state(struct client *cl, char **end_p); -int decrypt_get_number(char *start_p, char **end_p); -void decrypt_set_base_info(struct client *cl, char *start_p, char **end_p); -void decrypt_set_position_whose_turn(struct client *cl, char *start_p, - char **end_p); -void decrypt_set_card_queue(struct client *cl, char *start_p, char **end_p); -void decrypt_set_card_acceptance_status(struct client *cl, char *start_p); -void decrypt_set_spectator_mode(struct client *cl, char *start_p); -void decrypt_set_durak_position(struct client *cl, char *start_p); - -#endif diff --git a/client/printing_game_frames.c b/client/printing_game_frames.c deleted file mode 100644 index 6b2131a..0000000 --- a/client/printing_game_frames.c +++ /dev/null @@ -1,183 +0,0 @@ -#include "printing_game_frames.h" -#include "card_stack.h" - -#include -#include - -void pgf_new_frame() -{ - int i; - - for(i = 0; i < 25; ++i) - printf("\n"); -} - -void pgf_welcome() -{ - printf("======================================================\n" - " Welcome!\n" - "You're playing siege durak/Podkidnoy (Throw-in) durak\n\n" - "If you have any questions, you can contact by email to\n" - " m@scratko.xyz\n" - "======================================================\n"); -} - -void pgf_connection(int status) -{ - if(status) - printf("Connection to the server has been successfully established\n"); - else - printf("Sorry, failed to connect to the server\n"); -} - -void pgf_first_player() -{ - printf("=======================================\n" - " You're the first player\n" - "Waiting for other players to connect...\n" - " Press q to exit\n" - "=======================================\n"); -} - -void pgf_confirmation_waiting(int total_players) -{ - printf("=======================================\n" - " To start game press key\n" - " Connected players: %u/8\n" - " Press q to exit\n" - "=======================================\n", total_players); -} - -static int move_indication_condition(const struct client *cl, - int current_player) -{ - return - current_player == cl->position_whose_turn && - (cl->state == attack_expectation || cl->state == defense_expectation || - cl->state == attack || cl->state == defense || - (cl->state == spectator && (cl->sp_mode == spectator_attack || - cl->sp_mode == spectator_defense))); -} - -void pgf_table(struct client *cl) -{ - struct card_stack_item *deck = NULL; - int i; - - for(i = 0; i <= cl->cc.number_arr_idx; ++i) - printf("<%s %u %c> ", cl->player_position == i+1 ? "YOU" : " ", - cl->cc.number_arr[i], move_indication_condition(cl, i+1) ? - '*' : ' '); - printf(" %s [ %u ]", cl->trump_card, cl->total_cards_left); - printf("\n\n\n"); - /* ================= cards on table ================ */ - for(i = 0; i <= cl->cot.card_arr_idx; i += 3) - printf(" %s %s %s\n", cl->cot.card_arr[i], cl->cot.card_arr[i+1], - cl->cot.card_arr[i+2]); - /* for the defender, the cards in the queue are hidden */ - if(cl->state == defense_expectation || cl->state == spectator) - for(i = 0; i <= cl->cq.card_arr_idx; ++i) - printf(" %s \\ -\n", cl->cq.card_arr[i]); - if(cl->cot.card_arr_idx != -1) - printf("\n\n"); - /* ================ player's deck ================ */ - deck = cl->deck; - i = 0; - while(deck) { - printf("%c: %s ", deck->tip, deck->str); - ++i; - /* line break every 6 cards */ - if(!(i % 6)) - printf("\n"); - deck = deck->next; - } - if(cl->deck) - printf("\n"); -} - -void pgf_suggestions(struct client *cl) -{ - struct card_stack_item *deck = NULL; - - printf("qq - exit the game\n"); - if(cl->state == attack || cl->state == tossing) - printf("you can specify more than one card\n"); - deck = cl->deck; - while(deck) { - if(deck->is_usable) - printf("%c", deck->tip); - deck = deck->next; - } - printf(" > "); - fflush(stdout); -} - -void pgf_select_idle_mode_message(enum client_states state) -{ - switch(state) { - case attack_expectation: - printf("waiting for the attacker's turn\n"); - break; - case defense_expectation: - printf("waiting for the defender's turn\n"); - break; - case tossing_expectation: - printf("waiting for other players to toss cards\n"); - break; - default: - {} - } -} - -void pgf_card_acceptance_status(int all_input_cards_accepted) -{ - if(all_input_cards_accepted) - printf("all cards accepted\n"); - else - printf("not all cards were accepted\n"); -} - -void pgf_tossing_limit_status() -{ - printf("the cards were not accepted.\n" - "The other players have already tossed cards.\n"); -} - -void pgf_spectator_mode(enum spectator_mode sp_mode) -{ - printf("spectator mode\n"); - if(sp_mode == spectator_attack) - printf("attacker makes a move\n"); - else if(sp_mode == spectator_defense) - printf("defender make a move\n"); - else if(sp_mode == spectator_tossing) - printf("players toss in extra cards\n"); - else - printf("round result\n"); -} - -void pgf_game_result(int durak_position) -{ - printf("=======================================\n" - " END OF GAME\n" - " player number %u is durak\n" - "=======================================\n", durak_position); -} - -static void clear_input() -{ - int key; - - while((key = getchar()) != '\n') - {} -} - -void pgf_disconnect() -{ - char key; - - printf("Server connection disconnected\n" - "Press to close program\n"); - while((key = getchar()) != '\n') - clear_input(); -} diff --git a/client/printing_game_frames.h b/client/printing_game_frames.h deleted file mode 100644 index 9cbb231..0000000 --- a/client/printing_game_frames.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef PRINTING_GAME_FRAMES_H_SENTRY -#define PRINTING_GAME_FRAMES_H_SENTRY - -#include "client.h" - -void pgf_new_frame(); -void pgf_welcome(); -void pgf_connection(int status); -void pgf_first_player(); -void pgf_confirmation_waiting(int total_players); -void pgf_table(struct client *cl); -void pgf_suggestions(struct client *cl); -void pgf_select_idle_mode_message(enum client_states state); -void pgf_card_acceptance_status(int all_input_cards_accepted); -void pgf_tossing_limit_status(); -void pgf_spectator_mode(enum spectator_mode sp_mode); -void pgf_game_result(int durak_position); -void pgf_disconnect(); - -#endif diff --git a/client/verification_client_input.c b/client/verification_client_input.c deleted file mode 100644 index fa88b54..0000000 --- a/client/verification_client_input.c +++ /dev/null @@ -1,115 +0,0 @@ -#include "verification_client_input.h" -#include "card_handling.h" -#include "printing_game_frames.h" - -#include -#include -#include - -enum { - output_buffer_size = 1024 -}; - -static char output_buffer[output_buffer_size]; - -/* - * 1 - response received - * 0 - waiting for re-entry of data - */ -int vci_confirmation_waiting(int fd, char *buffer) -{ - if(buffer[0] == '\n') { - write(fd, buffer, 1); - return 1; - } - else - printf("please press enter\n"); - return 0; -} - -/* - * example: A#\A^\Av\'\n' - * or (while tossing): '\n' - */ -int vci_attack_or_tossing(int fd, char *buffer, player_cards deck, - enum client_states state) -{ - int rank = 0, i, free_pos = 0; - char * card = NULL; - - if(state == tossing && buffer[0] == '\n') { /* cancel card tossing */ - write(fd, buffer, 1); - printf("skipping the card toss\n"); - return 1; - } - - for(i = 0; buffer[i] != '\n'; ++i) { - /* some symbols */ - if(state == attack && buffer[1] != '\n') { - card = card_search_by_marked_letter(deck, buffer[i]); - if(card) { - if(!rank) - rank = convert_rank_to_int(card); - if(rank != convert_rank_to_int(card)) { - printf("incorrect input\n> "); - fflush(stdout); - return 0; - } - } - /* one symbol inputed? */ - } else if(state == attack && buffer[1] == '\n') - card = card_search_by_unmarked_letter(deck, buffer[i]); - else if(state == tossing) - card = card_search_by_marked_letter(deck, buffer[i]); - - if(!card) { - printf("incorrect input\n> "); - fflush(stdout); - return 0; - } - strncpy(output_buffer + free_pos, card, strlen(card)); - free_pos += strlen(card); - output_buffer[free_pos] = '\\'; - ++free_pos; - } - if(state == attack && buffer[0] == '\n') { - printf("incorrect input\n> "); - fflush(stdout); - return 0; - } - output_buffer[free_pos] = '\n'; - write(fd, output_buffer, free_pos+1); - return 1; -} - -/* - * example: 5v'\n' - * or: '\n' - */ -int vci_defense(int fd, char *buffer, player_cards deck) -{ - int last_idx; - char *card = NULL; - - if(buffer[0] == '\n') { /* the player does not want to keep the defense */ - write(fd, buffer, 1); - printf("accepting cards\n"); - return 1; - } - if(buffer[1] != '\n') { - printf("incorrect input\n> "); - fflush(stdout); - return 0; - } - if((card = card_search_by_marked_letter(deck, buffer[0])) != NULL) { - strcpy(output_buffer, card); - last_idx = strlen(card); - output_buffer[last_idx] = '\n'; - write(fd, output_buffer, last_idx+1); - return 1; - } - /* card is not found in the deck or cannot be used */ - printf("incorrect input\n> "); - fflush(stdout); - return 0; -} diff --git a/client/verification_client_input.h b/client/verification_client_input.h deleted file mode 100644 index 690f36e..0000000 --- a/client/verification_client_input.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef VERIFICATION_CLIENT_INPUT_H_SENTRY -#define VERIFICATION_CLIENT_INPUT_H_SENTRY - -#include "card_stack.h" -#include "client.h" - -int vci_confirmation_waiting(int fd, char *buffer); -int vci_attack_or_tossing(int fd, char *buffer, player_cards deck, - enum client_states state); -int vci_defense(int fd, char *buffer, player_cards deck); - -#endif diff --git a/linux_client/Makefile b/linux_client/Makefile new file mode 100644 index 0000000..2e2ce88 --- /dev/null +++ b/linux_client/Makefile @@ -0,0 +1,23 @@ +SRCMODULES = card_handling.c card_stack.c data_decryption.c\ + printing_game_frames.c verification_client_input.c client.c +OBJMODULES = $(SRCMODULES:.c=.o) +CC = gcc +CFLAGS = -Wall -g -c + +all: client + +%.o: %.с %.h + $(CC) $(CFLAGS) $< -o $@ + +client: $(OBJMODULES) + $(CC) $(LIBS) $^ -o $@ + +ifneq (clean, $(MAKECMDGOALS)) +-include deps.mk +endif + +deps.mk: $(SRCMODULES) + $(CC) -MM $^ > $@ + +clean: + rm -f *.o client diff --git a/linux_client/card_handling.c b/linux_client/card_handling.c new file mode 100644 index 0000000..011ad40 --- /dev/null +++ b/linux_client/card_handling.c @@ -0,0 +1,64 @@ +#include "card_handling.h" + +#include +#include + +int convert_rank_to_int(const char *card) +{ + int length; + char str_rank[2]; + + length = strlen(card); + /* 10 - the only one of its kind */ + if(length == 3) + return 10; + + str_rank[0] = card[0]; + str_rank[1] = '\0'; + + switch(card[0]) { + case 'J': + return 11; + case 'Q': + return 12; + case 'K': + return 13; + case 'A': + return 14; + default: + return strtol(str_rank, NULL, 10); + } + return 0; +} + +int is_card_beaten(const char *attack_card, const char *defend_card, + const char *trump_suit) +{ + int length, attack_rank, defend_rank; + const char *attack_suit, *defend_suit; + + length = strlen(attack_card); + attack_suit= attack_card + length - 1; + length = strlen(defend_card); + defend_suit = defend_card + length - 1; + + /* suits matched */ + if(!strcmp(attack_suit, defend_suit)) { + attack_rank = convert_rank_to_int(attack_card); + defend_rank = convert_rank_to_int(defend_card); + if(defend_rank > attack_rank) + return 1; + /* defender has a trump suit */ + } else if(!strcmp(defend_suit, trump_suit)) + return 1; + return 0; +} + +int check_matched_ranks(const char *attack_card, const char *not_defender_card) +{ + int attack_rank, not_defender_rank; + + attack_rank = convert_rank_to_int(attack_card); + not_defender_rank = convert_rank_to_int(not_defender_card); + return attack_rank == not_defender_rank; +} diff --git a/linux_client/card_handling.h b/linux_client/card_handling.h new file mode 100644 index 0000000..8dcb072 --- /dev/null +++ b/linux_client/card_handling.h @@ -0,0 +1,9 @@ +#ifndef CARD_HANDLING_H_SENTRY +#define CARD_HANDLING_H_SENTRY + +int convert_rank_to_int(const char *card); +int is_card_beaten(const char *attack_card, const char *defend_card, + const char *trump_suit); +int check_matched_ranks(const char *attack_card, const char *not_defender_card); + +#endif diff --git a/linux_client/card_stack.c b/linux_client/card_stack.c new file mode 100644 index 0000000..fc4057c --- /dev/null +++ b/linux_client/card_stack.c @@ -0,0 +1,173 @@ +#include "card_stack.h" +#include "card_handling.h" +#include "client.h" + +#include +#include + +void init_stack(player_cards *deck) +{ + *deck = NULL; +} + +void push_stack(player_cards *deck, char *str) +{ + struct card_stack_item *tmp = malloc(sizeof(struct card_stack_item)); + tmp->str = str; + tmp->tip = ' '; + tmp->is_usable = 0; + tmp->next = *deck; + *deck = tmp; +} + +void clear_stack(player_cards *deck) +{ + struct card_stack_item *tmp; + + while(*deck) { + tmp = *deck; + *deck = tmp->next; + if(tmp->str) + free(tmp->str); + free(tmp); + } +} + +int is_empty_stack(player_cards deck) +{ + return deck == NULL; +} + +void add_hint_letters_stack(player_cards deck) +{ + unsigned char letter = 'a'; + + while(deck) { + deck->tip = letter; + ++letter; + if(letter > 'z') + letter = 'A'; + deck = deck->next; + } +} + +static int check_no_attackers_cards_marked(player_cards deck) +{ + while(deck) { + if(deck->is_usable) + return 0; + deck = deck->next; + } + return 1; +} + +static void mark_all_card_stack(player_cards deck) +{ + player_cards tmp_deck = deck; + + while(tmp_deck) { + tmp_deck->is_usable = 1; + tmp_deck = tmp_deck->next; + } +} + +static void clear_is_usable_status(player_cards deck) +{ + while(deck) { + if(deck->is_usable) + deck->is_usable = 0; + deck = deck->next; + } +} + +void mark_card_for_attackers_stack(player_cards deck) +{ + char *card = NULL, *found_card; + struct card_stack_item *search_deck = NULL; + int target_rank, found_rank; + player_cards tmp_begin_deck = deck; + + clear_is_usable_status(deck); + while(deck) { + /* is the card already marked? */ + if(deck->is_usable) { + deck = deck->next; + continue; + } + card = deck->str; + target_rank = convert_rank_to_int(card); + search_deck = deck->next; + while(search_deck) { + if(search_deck->is_usable) { + search_deck = search_deck->next; + continue; + } + found_card = search_deck->str; + found_rank = convert_rank_to_int(found_card); + if(found_rank == target_rank) { + deck->is_usable = 1; + search_deck->is_usable = 1; + } + search_deck = search_deck->next; + } + deck = deck->next; + } + if(check_no_attackers_cards_marked(tmp_begin_deck)) + mark_all_card_stack(tmp_begin_deck); +} + +void mark_card_for_defenders_stack(player_cards deck, + struct cards_on_table *cot, char *trump_suit) +{ + int i; + + while(deck) { + for(i = 0; i <= cot->card_arr_idx; ++i) + if(((i == 0 || !(i % 3)) && cot->card_arr[i+2][0] == '-') && + is_card_beaten(cot->card_arr[i], deck->str, trump_suit)) + { + deck->is_usable = 1; + break; + } + deck = deck->next; + } +} + +void mark_card_for_tossing_stack(player_cards deck, struct cards_on_table *cot) +{ + int i; + + while(deck) { + for(i = 0; i <= cot->card_arr_idx; ++i) { + if(cot->card_arr[i][0] == '-' || cot->card_arr[i][0] == '\\') + continue; + if(check_matched_ranks(cot->card_arr[i], deck->str)) { + deck->is_usable = 1; + break; + } + } + deck = deck->next; + } +} + +char* card_search_by_marked_letter(player_cards deck, int letter) +{ + while(deck) { + if(deck->tip == letter && deck->is_usable) { + deck->is_usable = 0; + return deck->str; + } + deck = deck->next; + } + return NULL; +} + +char* card_search_by_unmarked_letter(player_cards deck, int letter) +{ + while(deck) { + if(deck->tip == letter) + return deck->str; + deck = deck->next; + } + return NULL; +} diff --git a/linux_client/card_stack.h b/linux_client/card_stack.h new file mode 100644 index 0000000..0daf40a --- /dev/null +++ b/linux_client/card_stack.h @@ -0,0 +1,30 @@ +#ifndef CARD_STACK_H_SENTRY +#define CARD_STACK_H_SENTRY + +struct card_stack_item { + char *str; + struct card_stack_item *next; + /* like a, b, c etc */ + unsigned char tip; + /* 1 - can be used, 0 - can't take it */ + int is_usable; +}; + +typedef struct card_stack_item* player_cards; + +void init_stack(player_cards *deck); +void push_stack(player_cards *deck, char *str); +void clear_stack(player_cards *deck); +int is_empty_stack(player_cards deck); +void add_hint_letters_stack(player_cards deck); +void mark_card_for_attackers_stack(player_cards deck); + +struct cards_on_table; +void mark_card_for_defenders_stack(player_cards deck, + struct cards_on_table *cot, + char* trump_suit); +void mark_card_for_tossing_stack(player_cards deck, struct cards_on_table *cot); +char* card_search_by_marked_letter(player_cards deck, int letter); +char* card_search_by_unmarked_letter(player_cards deck, int letter); + +#endif diff --git a/linux_client/client.c b/linux_client/client.c new file mode 100644 index 0000000..2115b98 --- /dev/null +++ b/linux_client/client.c @@ -0,0 +1,321 @@ +#include +#include +#include +#include +#include +#include +#include /* for debug */ + +#include "client.h" +#include "data_decryption.h" +#include "printing_game_frames.h" +#include "verification_client_input.h" + +static const char *ip = "109.107.161.30"; +static const int port = 1025; + +static void clean_up_resources(struct client *cl) +{ + free(cl->cc.number_arr); + clear_stack(&cl->deck); +} + +static void get_data_from_server(struct client *cl, fd_set *readfds) +{ + int i; + int update_info = 0; + /* pointer to delimeter (:) that is behind extract data */ + char *end_p; + + if(FD_ISSET(cl->fd, readfds)) { + if(!cl->data_left_in_buffer) { + cl->data_left_in_buffer = read(cl->fd, cl->buffer, max_buffer_size); + /* end of file -- closed connection (from server) */ + if(!cl->data_left_in_buffer) { + clean_up_resources(cl); + exit(0); + } +#ifdef DEBUG + printf("%d\n", cl->data_left_in_buffer); + for(i = 0; i < cl->data_left_in_buffer; ++i) + putchar(cl->buffer[i]); + putchar('\n'); +#endif + } + decrypt_set_state(cl, &end_p); + update_info = 1; + cl->display_new_frame = 1; + cl->pending_server_response = 0; + } + if(update_info) { + switch(cl->state) { + case first_player: + break; + case confirmation_waiting: + cl->total_players = decrypt_get_number(end_p+1, &end_p); + break; + case display_only_table: + case tossing_expectation: + case tossing: + /* last arg will contain pointer to '\n' */ + decrypt_set_base_info(cl, end_p+1, &end_p); + break; + case attack: + case defense: + case attack_expectation: + decrypt_set_base_info(cl, end_p+1, &end_p); + decrypt_set_position_whose_turn(cl, end_p+1, &end_p); + break; + case defense_expectation: + case spectator: + decrypt_set_base_info(cl, end_p+1, &end_p); + decrypt_set_position_whose_turn(cl, end_p+1, &end_p); + decrypt_set_card_queue(cl, end_p+1, &end_p); + if(cl->state == spectator) + decrypt_set_spectator_mode(cl, end_p+1); + break; + case card_acceptance_status: + decrypt_set_card_acceptance_status(cl, end_p+1); + break; + case result: + decrypt_set_durak_position(cl, end_p+1); + break; + /* no data to extract */ + case tossing_limit_status: + case disconnect: + default: + {} + } + for(i = 0; cl->buffer[i] != '\n'; ++i) + {} + if((cl->buffer+i - cl->buffer + 1) != cl->data_left_in_buffer) { + cl->data_left_in_buffer -= i + 1; + memmove(cl->buffer, cl->buffer+i+1, cl->data_left_in_buffer); + } else + cl->data_left_in_buffer = 0; + } +} + +static void prepare_tips_for_client(struct client *cl) +{ + char *trump_suit = NULL; + + if(!is_empty_stack(cl->deck)) + add_hint_letters_stack(cl->deck); + switch(cl->state) { + case attack: + mark_card_for_attackers_stack(cl->deck); + break; + case defense: + trump_suit = cl->trump_card + strlen(cl->trump_card) - 1; + mark_card_for_defenders_stack(cl->deck, &cl->cot, trump_suit); + break; + case tossing: + mark_card_for_tossing_stack(cl->deck, &cl->cot); + break; + default: + {} + } +} + +static void change_client_frame(struct client *cl) +{ + if(cl->display_new_frame) { + pgf_new_frame(); + switch(cl->state) { + case first_player: + pgf_first_player(); + break; + case confirmation_waiting: + pgf_confirmation_waiting(cl->total_players); + break; + case display_only_table: + case attack_expectation: + case defense_expectation: + case tossing_expectation: + pgf_table(cl); + if(cl->state == attack_expectation || + cl->state == defense_expectation || + cl->state == tossing_expectation) + { + pgf_select_idle_mode_message(cl->state); + } + break; + case spectator: + pgf_table(cl); + pgf_spectator_mode(cl->sp_mode); + break; + case attack: + case defense: + case tossing: + pgf_table(cl); + pgf_suggestions(cl); + break; + case card_acceptance_status: + pgf_card_acceptance_status(cl->all_input_cards_accepted); + break; + case tossing_limit_status: + pgf_tossing_limit_status(); + break; + case result: + pgf_game_result(cl->durak_position); + break; + case disconnect: + pgf_disconnect(); + break; + case none: + {} + } + cl->display_new_frame = 0; + } +} + +static int check_users_exit(enum client_states state, const char *buffer, + int size) +{ + if((state == first_player || state == confirmation_waiting) && + size == 2 && (buffer[0] == 'q' && buffer[1] == '\n')) + return 1; + + return size == 3 && !strncmp(buffer, "qq", 2) && buffer[2] == '\n'; +} + +static void send_data_to_server(struct client *cl, fd_set *readfds) +{ + int input_result = 0, data_size; + + if(FD_ISSET(0, readfds)) { /* 0 - stdin */ + data_size = read(0, cl->buffer, max_buffer_size); + if(check_users_exit(cl->state, cl->buffer, data_size)) { + close(cl->fd); + clean_up_resources(cl); + pgf_disconnect(); + exit(0); + } + + switch(cl->state) { + case confirmation_waiting: + input_result = vci_confirmation_waiting(cl->fd, cl->buffer); + break; + case attack: + case tossing: + input_result = vci_attack_or_tossing(cl->fd, cl->buffer, cl->deck, + cl->state); + break; + case defense: + input_result = vci_defense(cl->fd, cl->buffer, cl->deck); + break; + default: + {} + } + /* if 0, then re-tracking the input client */ + if(input_result) + cl->pending_server_response = 1; + } +} + +static int check_tracking_client_input(enum client_states state, + int pending_server_response) +{ + if(pending_server_response) + return 0; + + switch(state) { + case first_player: + case confirmation_waiting: + case attack: + case defense: + case tossing: + return 1; + default: + return 0; + } + return 0; +} + +int main_loop(struct client *cl) +{ + int select_result; + fd_set readfds; + + for(;;) { + FD_ZERO(&readfds); + + FD_SET(cl->fd, &readfds); + if(check_tracking_client_input(cl->state, cl->pending_server_response)) + FD_SET(0, &readfds); + + if(!cl->data_left_in_buffer) { + select_result = select(cl->fd + 1, &readfds, NULL, NULL, NULL); + if(select_result == -1) + return 2; + } + get_data_from_server(cl, &readfds); + prepare_tips_for_client(cl); + if(cl->display_new_frame) + change_client_frame(cl); + send_data_to_server(cl, &readfds); + } +} + +static void init_client(struct client *cl) +{ + cl->fd = -1; + cl->state = none; + cl->total_players = 0; + cl->total_cards_left = 0; + cl->player_position = 0; + cl->cc.number_arr = NULL; + cl->cc.number_arr_idx = -1; + cl->cot.card_arr_idx = -1; + cl->deck = NULL; + cl->pending_server_response = 0; + cl->cq.card_arr_idx = -1; + cl->position_whose_turn = 0; + cl->display_new_frame = 0; + cl->all_input_cards_accepted = 0; + cl->data_left_in_buffer = 0; + cl->sp_mode = spectator_table; + cl->durak_position = 0; +} + +/* + * 0 - failure + * 1 - success + */ +static int connect_to_server(struct client *cl) +{ + int connect_result; + struct sockaddr_in addr; + + cl->fd = socket(AF_INET, SOCK_STREAM, 0); + + if(cl->fd == -1) + return 0; + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = inet_addr(ip); + addr.sin_port = htons(port); + + connect_result = connect(cl->fd, (struct sockaddr*) &addr, sizeof(addr)); + if(connect_result == -1) + return 0; + return 1; +} + +int main() +{ + struct client cl; + + pgf_new_frame(); + pgf_welcome(); + sleep(2); + init_client(&cl); + if(!connect_to_server(&cl)) { + pgf_new_frame(); + pgf_connection(0); + return 1; + } + pgf_new_frame(); + pgf_connection(1); + return main_loop(&cl); +} diff --git a/linux_client/client.h b/linux_client/client.h new file mode 100644 index 0000000..12ec619 --- /dev/null +++ b/linux_client/client.h @@ -0,0 +1,84 @@ +#ifndef CLIENT_H_SENTRY +#define CLIENT_H_SENTRY + +#include "card_stack.h" + +enum { + max_buffer_size = 4096, + max_cot_arr_size = 18, + card_size = 4, + max_cq_arr_size = 5 +}; + +enum client_states { + none, + first_player, + confirmation_waiting, + /* in game */ + display_only_table, + attack_expectation, + defense_expectation, + tossing_expectation, + attack, + defense, + tossing, + card_acceptance_status, + tossing_limit_status, + spectator, + result, + disconnect +}; + +enum spectator_mode { + spectator_attack, + spectator_defense, + spectator_tossing, + spectator_table +}; + +struct cards_on_table { + /* + * example: 2^ \ - 10# \ K# + * [2 ^ '\0' ][1 0 # '\0'][\ '\0' ] + */ + char card_arr[max_cot_arr_size][card_size]; + /* index of the last filled element */ + int card_arr_idx; +}; + +struct card_queue { + char card_arr[max_cq_arr_size][card_size]; + int card_arr_idx; +}; + +struct card_count { + int *number_arr; + /* + * will only store the initial number of players + * (idx: total_players - 1 + */ + int number_arr_idx; +}; + +struct client { + int fd; + enum client_states state; + char buffer[max_buffer_size]; + int total_players; + int total_cards_left; + int player_position; + char trump_card[4]; + struct card_count cc; + struct cards_on_table cot; + player_cards deck; + int pending_server_response; + struct card_queue cq; + int position_whose_turn; + int display_new_frame; + int all_input_cards_accepted; + int data_left_in_buffer; + enum spectator_mode sp_mode; + int durak_position; +}; + +#endif diff --git a/linux_client/data_decryption.c b/linux_client/data_decryption.c new file mode 100644 index 0000000..1054c45 --- /dev/null +++ b/linux_client/data_decryption.c @@ -0,0 +1,181 @@ +#include "data_decryption.h" +#include "client.h" +#include "card_stack.h" + +#include +#include +#include + +void decrypt_set_state(struct client *cl, char **end_p) +{ + enum client_states found_state; + + found_state = (enum client_states) strtol(cl->buffer, end_p, 10); + cl->state = found_state; +} + +/* return convert number from str */ +int decrypt_get_number(char *start_p, char **end_p) +{ + return strtol(start_p, end_p, 10); +} + +static void decrypt_set_trump_card(struct client *cl, char *start_p, + char **end_p) +{ + int i; + + for(i = 0; *start_p != ':'; ++start_p, ++i) + cl->trump_card[i] = *start_p; + cl->trump_card[i] = '\0'; + *end_p = start_p; +} + +void decrypt_set_base_info(struct client *cl, char *start_p, char **end_p) +{ + int i, j; + char tmp_card[4]; + char *card = NULL; + + cl->total_players = decrypt_get_number(start_p, end_p); + cl->total_cards_left = decrypt_get_number(*end_p+1, end_p); + cl->player_position = decrypt_get_number(*end_p+1, end_p); + decrypt_set_trump_card(cl, *end_p+1, end_p); + + if(!cl->cc.number_arr) { + cl->cc.number_arr = malloc(cl->total_players * sizeof(int)); + /* + * memorize the size of the array, + * because later the number of players may change (client disconnection) + */ + cl->cc.number_arr_idx = cl->total_players-1; + } + + /* ============== extraction quantity of cards =============== */ + + /* numbers are separated by '\' */ + i = 0; + do { + cl->cc.number_arr[i] = decrypt_get_number(*end_p+1, end_p); + ++i; + } while(**end_p != ':'); + + /* ============== extraction cards on table =============== */ + i = 0, j = 0; + for(start_p = *end_p + 1; *start_p != ':'; ++start_p) { + /* empty table */ + if(*start_p == '=') { + /* to delimiter : */ + ++start_p; + i = -1; + break; + } + /* delimiter between cards on table */ + if(*start_p == '+') { + cl->cot.card_arr[i][j] = '\0'; + ++i; + j = 0; + continue; + } + cl->cot.card_arr[i][j] = *start_p; + ++j; + } + /* for the last card we need to add a null character */ + if(i != -1) + cl->cot.card_arr[i][j] = '\0'; + cl->cot.card_arr_idx = i; + + /* ================= extraction player's deck ================*/ + /* clear previous stack */ + if(cl->deck) + clear_stack(&cl->deck); + init_stack(&cl->deck); + + for(++start_p, i = 0; *start_p != ':' && *start_p != '\n'; ++start_p) { + /* empty deck */ + if(*start_p == '=') { + i = -1; + /* to delimiter ':' or '\n' */ + ++start_p; + break; + } + /* delimiter */ + if(*start_p == '\\') { + tmp_card[i] = '\0'; + card = malloc(strlen(tmp_card) + 1); + strcpy(card, tmp_card); + push_stack(&cl->deck, card); + i = 0; + continue; + } + tmp_card[i] = *start_p; + ++i; + } + if(i != -1) { + tmp_card[i] = '\0'; + card = malloc(strlen(tmp_card) + 1); + strcpy(card, tmp_card); + push_stack(&cl->deck, card); + } + *end_p = start_p; +} + +void decrypt_set_position_whose_turn(struct client *cl, char *start_p, + char **end_p) +{ + cl->position_whose_turn = decrypt_get_number(start_p, end_p); +} + +void decrypt_set_card_queue(struct client *cl, char *start_p, char **end_p) +{ + int i, j; + + for(i = 0, j = 0; *start_p != ':' && *start_p != '\n'; ++start_p) { + /* empty queue */ + if(*start_p == '=') { + i = -1; + ++start_p; + break; + } + /* delimiter '\' */ + if(*start_p == '\\') { + cl->cq.card_arr[i][j] = '\0'; + j = 0; + ++i; + continue; + } + cl->cq.card_arr[i][j] = *start_p; + ++j; + } + /* last element */ + if(i != -1) + cl->cq.card_arr[i][j] = '\0'; + cl->cq.card_arr_idx = i; + *end_p = start_p; +} + +void decrypt_set_card_acceptance_status(struct client *cl, char *start_p) +{ + int i; + char tmp_buffer[8]; + + for(i = 0; *start_p != '\n'; ++start_p, ++i) + tmp_buffer[i] = *start_p; + tmp_buffer[i] = '\0'; + if(!strcmp(tmp_buffer, "all")) { + cl->all_input_cards_accepted = 1; + return; + } + if(!strcmp(tmp_buffer, "not all")) + cl->all_input_cards_accepted = 0; +} + +void decrypt_set_spectator_mode(struct client *cl, char *start_p) +{ + cl->sp_mode = (enum spectator_mode) decrypt_get_number(start_p, NULL); +} + +void decrypt_set_durak_position(struct client *cl, char *start_p) +{ + cl->durak_position = decrypt_get_number(start_p, NULL); +} diff --git a/linux_client/data_decryption.h b/linux_client/data_decryption.h new file mode 100644 index 0000000..08772fe --- /dev/null +++ b/linux_client/data_decryption.h @@ -0,0 +1,16 @@ +#ifndef DATA_DECRYPTION_H_SENTRY +#define DATA_DECRYPTION_H_SENTRY + +#include "client.h" + +void decrypt_set_state(struct client *cl, char **end_p); +int decrypt_get_number(char *start_p, char **end_p); +void decrypt_set_base_info(struct client *cl, char *start_p, char **end_p); +void decrypt_set_position_whose_turn(struct client *cl, char *start_p, + char **end_p); +void decrypt_set_card_queue(struct client *cl, char *start_p, char **end_p); +void decrypt_set_card_acceptance_status(struct client *cl, char *start_p); +void decrypt_set_spectator_mode(struct client *cl, char *start_p); +void decrypt_set_durak_position(struct client *cl, char *start_p); + +#endif diff --git a/linux_client/printing_game_frames.c b/linux_client/printing_game_frames.c new file mode 100644 index 0000000..6b2131a --- /dev/null +++ b/linux_client/printing_game_frames.c @@ -0,0 +1,183 @@ +#include "printing_game_frames.h" +#include "card_stack.h" + +#include +#include + +void pgf_new_frame() +{ + int i; + + for(i = 0; i < 25; ++i) + printf("\n"); +} + +void pgf_welcome() +{ + printf("======================================================\n" + " Welcome!\n" + "You're playing siege durak/Podkidnoy (Throw-in) durak\n\n" + "If you have any questions, you can contact by email to\n" + " m@scratko.xyz\n" + "======================================================\n"); +} + +void pgf_connection(int status) +{ + if(status) + printf("Connection to the server has been successfully established\n"); + else + printf("Sorry, failed to connect to the server\n"); +} + +void pgf_first_player() +{ + printf("=======================================\n" + " You're the first player\n" + "Waiting for other players to connect...\n" + " Press q to exit\n" + "=======================================\n"); +} + +void pgf_confirmation_waiting(int total_players) +{ + printf("=======================================\n" + " To start game press key\n" + " Connected players: %u/8\n" + " Press q to exit\n" + "=======================================\n", total_players); +} + +static int move_indication_condition(const struct client *cl, + int current_player) +{ + return + current_player == cl->position_whose_turn && + (cl->state == attack_expectation || cl->state == defense_expectation || + cl->state == attack || cl->state == defense || + (cl->state == spectator && (cl->sp_mode == spectator_attack || + cl->sp_mode == spectator_defense))); +} + +void pgf_table(struct client *cl) +{ + struct card_stack_item *deck = NULL; + int i; + + for(i = 0; i <= cl->cc.number_arr_idx; ++i) + printf("<%s %u %c> ", cl->player_position == i+1 ? "YOU" : " ", + cl->cc.number_arr[i], move_indication_condition(cl, i+1) ? + '*' : ' '); + printf(" %s [ %u ]", cl->trump_card, cl->total_cards_left); + printf("\n\n\n"); + /* ================= cards on table ================ */ + for(i = 0; i <= cl->cot.card_arr_idx; i += 3) + printf(" %s %s %s\n", cl->cot.card_arr[i], cl->cot.card_arr[i+1], + cl->cot.card_arr[i+2]); + /* for the defender, the cards in the queue are hidden */ + if(cl->state == defense_expectation || cl->state == spectator) + for(i = 0; i <= cl->cq.card_arr_idx; ++i) + printf(" %s \\ -\n", cl->cq.card_arr[i]); + if(cl->cot.card_arr_idx != -1) + printf("\n\n"); + /* ================ player's deck ================ */ + deck = cl->deck; + i = 0; + while(deck) { + printf("%c: %s ", deck->tip, deck->str); + ++i; + /* line break every 6 cards */ + if(!(i % 6)) + printf("\n"); + deck = deck->next; + } + if(cl->deck) + printf("\n"); +} + +void pgf_suggestions(struct client *cl) +{ + struct card_stack_item *deck = NULL; + + printf("qq - exit the game\n"); + if(cl->state == attack || cl->state == tossing) + printf("you can specify more than one card\n"); + deck = cl->deck; + while(deck) { + if(deck->is_usable) + printf("%c", deck->tip); + deck = deck->next; + } + printf(" > "); + fflush(stdout); +} + +void pgf_select_idle_mode_message(enum client_states state) +{ + switch(state) { + case attack_expectation: + printf("waiting for the attacker's turn\n"); + break; + case defense_expectation: + printf("waiting for the defender's turn\n"); + break; + case tossing_expectation: + printf("waiting for other players to toss cards\n"); + break; + default: + {} + } +} + +void pgf_card_acceptance_status(int all_input_cards_accepted) +{ + if(all_input_cards_accepted) + printf("all cards accepted\n"); + else + printf("not all cards were accepted\n"); +} + +void pgf_tossing_limit_status() +{ + printf("the cards were not accepted.\n" + "The other players have already tossed cards.\n"); +} + +void pgf_spectator_mode(enum spectator_mode sp_mode) +{ + printf("spectator mode\n"); + if(sp_mode == spectator_attack) + printf("attacker makes a move\n"); + else if(sp_mode == spectator_defense) + printf("defender make a move\n"); + else if(sp_mode == spectator_tossing) + printf("players toss in extra cards\n"); + else + printf("round result\n"); +} + +void pgf_game_result(int durak_position) +{ + printf("=======================================\n" + " END OF GAME\n" + " player number %u is durak\n" + "=======================================\n", durak_position); +} + +static void clear_input() +{ + int key; + + while((key = getchar()) != '\n') + {} +} + +void pgf_disconnect() +{ + char key; + + printf("Server connection disconnected\n" + "Press to close program\n"); + while((key = getchar()) != '\n') + clear_input(); +} diff --git a/linux_client/printing_game_frames.h b/linux_client/printing_game_frames.h new file mode 100644 index 0000000..9cbb231 --- /dev/null +++ b/linux_client/printing_game_frames.h @@ -0,0 +1,20 @@ +#ifndef PRINTING_GAME_FRAMES_H_SENTRY +#define PRINTING_GAME_FRAMES_H_SENTRY + +#include "client.h" + +void pgf_new_frame(); +void pgf_welcome(); +void pgf_connection(int status); +void pgf_first_player(); +void pgf_confirmation_waiting(int total_players); +void pgf_table(struct client *cl); +void pgf_suggestions(struct client *cl); +void pgf_select_idle_mode_message(enum client_states state); +void pgf_card_acceptance_status(int all_input_cards_accepted); +void pgf_tossing_limit_status(); +void pgf_spectator_mode(enum spectator_mode sp_mode); +void pgf_game_result(int durak_position); +void pgf_disconnect(); + +#endif diff --git a/linux_client/verification_client_input.c b/linux_client/verification_client_input.c new file mode 100644 index 0000000..fa88b54 --- /dev/null +++ b/linux_client/verification_client_input.c @@ -0,0 +1,115 @@ +#include "verification_client_input.h" +#include "card_handling.h" +#include "printing_game_frames.h" + +#include +#include +#include + +enum { + output_buffer_size = 1024 +}; + +static char output_buffer[output_buffer_size]; + +/* + * 1 - response received + * 0 - waiting for re-entry of data + */ +int vci_confirmation_waiting(int fd, char *buffer) +{ + if(buffer[0] == '\n') { + write(fd, buffer, 1); + return 1; + } + else + printf("please press enter\n"); + return 0; +} + +/* + * example: A#\A^\Av\'\n' + * or (while tossing): '\n' + */ +int vci_attack_or_tossing(int fd, char *buffer, player_cards deck, + enum client_states state) +{ + int rank = 0, i, free_pos = 0; + char * card = NULL; + + if(state == tossing && buffer[0] == '\n') { /* cancel card tossing */ + write(fd, buffer, 1); + printf("skipping the card toss\n"); + return 1; + } + + for(i = 0; buffer[i] != '\n'; ++i) { + /* some symbols */ + if(state == attack && buffer[1] != '\n') { + card = card_search_by_marked_letter(deck, buffer[i]); + if(card) { + if(!rank) + rank = convert_rank_to_int(card); + if(rank != convert_rank_to_int(card)) { + printf("incorrect input\n> "); + fflush(stdout); + return 0; + } + } + /* one symbol inputed? */ + } else if(state == attack && buffer[1] == '\n') + card = card_search_by_unmarked_letter(deck, buffer[i]); + else if(state == tossing) + card = card_search_by_marked_letter(deck, buffer[i]); + + if(!card) { + printf("incorrect input\n> "); + fflush(stdout); + return 0; + } + strncpy(output_buffer + free_pos, card, strlen(card)); + free_pos += strlen(card); + output_buffer[free_pos] = '\\'; + ++free_pos; + } + if(state == attack && buffer[0] == '\n') { + printf("incorrect input\n> "); + fflush(stdout); + return 0; + } + output_buffer[free_pos] = '\n'; + write(fd, output_buffer, free_pos+1); + return 1; +} + +/* + * example: 5v'\n' + * or: '\n' + */ +int vci_defense(int fd, char *buffer, player_cards deck) +{ + int last_idx; + char *card = NULL; + + if(buffer[0] == '\n') { /* the player does not want to keep the defense */ + write(fd, buffer, 1); + printf("accepting cards\n"); + return 1; + } + if(buffer[1] != '\n') { + printf("incorrect input\n> "); + fflush(stdout); + return 0; + } + if((card = card_search_by_marked_letter(deck, buffer[0])) != NULL) { + strcpy(output_buffer, card); + last_idx = strlen(card); + output_buffer[last_idx] = '\n'; + write(fd, output_buffer, last_idx+1); + return 1; + } + /* card is not found in the deck or cannot be used */ + printf("incorrect input\n> "); + fflush(stdout); + return 0; +} diff --git a/linux_client/verification_client_input.h b/linux_client/verification_client_input.h new file mode 100644 index 0000000..690f36e --- /dev/null +++ b/linux_client/verification_client_input.h @@ -0,0 +1,12 @@ +#ifndef VERIFICATION_CLIENT_INPUT_H_SENTRY +#define VERIFICATION_CLIENT_INPUT_H_SENTRY + +#include "card_stack.h" +#include "client.h" + +int vci_confirmation_waiting(int fd, char *buffer); +int vci_attack_or_tossing(int fd, char *buffer, player_cards deck, + enum client_states state); +int vci_defense(int fd, char *buffer, player_cards deck); + +#endif diff --git a/server/server.c b/server/server.c index 6a9075c..0017888 100644 --- a/server/server.c +++ b/server/server.c @@ -7,6 +7,7 @@ #include #include #include +#include #include "server.h" #include "server_data_processing.h" @@ -14,6 +15,12 @@ #include "card_stack.h" #include "card_queue.h" +void handler(int s) +{ + if(s == SIGPIPE) + signal(SIGPIPE, handler); +} + static void init_session(struct session *new_session, struct sockaddr_in *from, int fd, int player_position) { @@ -958,6 +965,7 @@ int main(int argc, char **argv) srand(time(NULL)); openlog("durak server", 0, LOG_USER); syslog(LOG_INFO, "daemon started"); + signal(SIGPIPE, handler); port = strtol(argv[1], NULL, 10); if(!init_server(&serv, port)) { diff --git a/server/server_data_processing.c b/server/server_data_processing.c index 41e4b08..40ce5e3 100644 --- a/server/server_data_processing.c +++ b/server/server_data_processing.c @@ -235,10 +235,14 @@ int get_cards_from_attacker(struct session *client, const char *stack_card = NULL; read_result = read_from_client(client->fd, client->buffer, max_buffer_size); + +#if DEBUG printf("%d\n", read_result); for(i = 0; i < read_result; ++i) putchar(client->buffer[i]); putchar('\n'); +#endif + if(!read_result) return read_result; for(i = 0, j = 0; i < read_result; ++i) { @@ -261,6 +265,7 @@ int get_cards_from_attacker(struct session *client, data_length = sprintf(output_buffer, "%d:%s\n", card_acceptance_status, all_cards_received_status ? "all" : "not all"); write_to_client(client->fd, data_length); + sleep(2); return read_result; } @@ -280,14 +285,21 @@ int get_card_from_defender(struct session *client, struct cards_on_table *cot, const char *stack_card = NULL; read_result = read_from_client(client->fd, client->buffer, max_buffer_size); + +#if DEBUG printf("%d\n", read_result); for(i = 0; i < read_result; ++i) putchar(client->buffer[i]); putchar('\n'); +#endif + if(!read_result) return read_result; - if(client->buffer[0] == '\n') + if(client->buffer[0] == '\n') { + /* the client sees “accepting cards” at the same time */ + sleep(2); return 1; + } for(i = 0; i < read_result && client->buffer[i] != '\n'; ++i) given_card[i] = client->buffer[i]; given_card[i] = '\0'; @@ -321,16 +333,23 @@ int get_cards_from_tossing_player(struct session *client, const char *stack_card = NULL; read_result = read_from_client(client->fd, client->buffer, max_buffer_size); + +#if DEBUG printf("%d\n", read_result); for(i = 0; i < read_result; ++i) putchar(client->buffer[i]); putchar('\n'); +#endif + if(!read_result) return read_result; /* cancellation */ - if(client->buffer[0] == '\n') + if(client->buffer[0] == '\n') { + /* the client sees “skipping the card toss” at the same time */ + sleep(2); return cancel; + } for(i = 0, j = 0; i < read_result; ++i) { if(client->buffer[i] == '\\') { @@ -363,6 +382,7 @@ int get_cards_from_tossing_player(struct session *client, data_length = sprintf(output_buffer, "%d:%s\n", card_acceptance_status, all_cards_received_status ? "all" : "not all"); write_to_client(client->fd, data_length); + sleep(2); if(!card_return_status) return answer_got; else diff --git a/windows_client/Makefile b/windows_client/Makefile new file mode 100644 index 0000000..497a488 --- /dev/null +++ b/windows_client/Makefile @@ -0,0 +1,22 @@ +SRCMODULES = card_handling.c card_stack.c data_decryption.c\ + printing_game_frames.c verification_client_input.c client.c +OBJMODULES = $(SRCMODULES:.c=.o) +CC = i686-w64-mingw32-gcc +CFLAGS = -Wall -g -c -I/usr/i686-w64-mingw-32/include + +STATIC = -static + +LIBS = -lws2_32 -L /usr/i686-w64-mingw-32/lib + +all: durak.exe + +%.o: %.с %.h + $(CC) $(CFLAGS) $< -o $@ + +durak.exe: $(OBJMODULES) + $(CC) $(STATIC) $(OBJMODULES) $(LIBS) -o $@ + +-include deps.mk + +deps.mk: $(SRCMODULES) + $(CC) -MM $^ > $@ diff --git a/windows_client/card_handling.c b/windows_client/card_handling.c new file mode 100644 index 0000000..011ad40 --- /dev/null +++ b/windows_client/card_handling.c @@ -0,0 +1,64 @@ +#include "card_handling.h" + +#include +#include + +int convert_rank_to_int(const char *card) +{ + int length; + char str_rank[2]; + + length = strlen(card); + /* 10 - the only one of its kind */ + if(length == 3) + return 10; + + str_rank[0] = card[0]; + str_rank[1] = '\0'; + + switch(card[0]) { + case 'J': + return 11; + case 'Q': + return 12; + case 'K': + return 13; + case 'A': + return 14; + default: + return strtol(str_rank, NULL, 10); + } + return 0; +} + +int is_card_beaten(const char *attack_card, const char *defend_card, + const char *trump_suit) +{ + int length, attack_rank, defend_rank; + const char *attack_suit, *defend_suit; + + length = strlen(attack_card); + attack_suit= attack_card + length - 1; + length = strlen(defend_card); + defend_suit = defend_card + length - 1; + + /* suits matched */ + if(!strcmp(attack_suit, defend_suit)) { + attack_rank = convert_rank_to_int(attack_card); + defend_rank = convert_rank_to_int(defend_card); + if(defend_rank > attack_rank) + return 1; + /* defender has a trump suit */ + } else if(!strcmp(defend_suit, trump_suit)) + return 1; + return 0; +} + +int check_matched_ranks(const char *attack_card, const char *not_defender_card) +{ + int attack_rank, not_defender_rank; + + attack_rank = convert_rank_to_int(attack_card); + not_defender_rank = convert_rank_to_int(not_defender_card); + return attack_rank == not_defender_rank; +} diff --git a/windows_client/card_handling.h b/windows_client/card_handling.h new file mode 100644 index 0000000..8dcb072 --- /dev/null +++ b/windows_client/card_handling.h @@ -0,0 +1,9 @@ +#ifndef CARD_HANDLING_H_SENTRY +#define CARD_HANDLING_H_SENTRY + +int convert_rank_to_int(const char *card); +int is_card_beaten(const char *attack_card, const char *defend_card, + const char *trump_suit); +int check_matched_ranks(const char *attack_card, const char *not_defender_card); + +#endif diff --git a/windows_client/card_stack.c b/windows_client/card_stack.c new file mode 100644 index 0000000..fc4057c --- /dev/null +++ b/windows_client/card_stack.c @@ -0,0 +1,173 @@ +#include "card_stack.h" +#include "card_handling.h" +#include "client.h" + +#include +#include + +void init_stack(player_cards *deck) +{ + *deck = NULL; +} + +void push_stack(player_cards *deck, char *str) +{ + struct card_stack_item *tmp = malloc(sizeof(struct card_stack_item)); + tmp->str = str; + tmp->tip = ' '; + tmp->is_usable = 0; + tmp->next = *deck; + *deck = tmp; +} + +void clear_stack(player_cards *deck) +{ + struct card_stack_item *tmp; + + while(*deck) { + tmp = *deck; + *deck = tmp->next; + if(tmp->str) + free(tmp->str); + free(tmp); + } +} + +int is_empty_stack(player_cards deck) +{ + return deck == NULL; +} + +void add_hint_letters_stack(player_cards deck) +{ + unsigned char letter = 'a'; + + while(deck) { + deck->tip = letter; + ++letter; + if(letter > 'z') + letter = 'A'; + deck = deck->next; + } +} + +static int check_no_attackers_cards_marked(player_cards deck) +{ + while(deck) { + if(deck->is_usable) + return 0; + deck = deck->next; + } + return 1; +} + +static void mark_all_card_stack(player_cards deck) +{ + player_cards tmp_deck = deck; + + while(tmp_deck) { + tmp_deck->is_usable = 1; + tmp_deck = tmp_deck->next; + } +} + +static void clear_is_usable_status(player_cards deck) +{ + while(deck) { + if(deck->is_usable) + deck->is_usable = 0; + deck = deck->next; + } +} + +void mark_card_for_attackers_stack(player_cards deck) +{ + char *card = NULL, *found_card; + struct card_stack_item *search_deck = NULL; + int target_rank, found_rank; + player_cards tmp_begin_deck = deck; + + clear_is_usable_status(deck); + while(deck) { + /* is the card already marked? */ + if(deck->is_usable) { + deck = deck->next; + continue; + } + card = deck->str; + target_rank = convert_rank_to_int(card); + search_deck = deck->next; + while(search_deck) { + if(search_deck->is_usable) { + search_deck = search_deck->next; + continue; + } + found_card = search_deck->str; + found_rank = convert_rank_to_int(found_card); + if(found_rank == target_rank) { + deck->is_usable = 1; + search_deck->is_usable = 1; + } + search_deck = search_deck->next; + } + deck = deck->next; + } + if(check_no_attackers_cards_marked(tmp_begin_deck)) + mark_all_card_stack(tmp_begin_deck); +} + +void mark_card_for_defenders_stack(player_cards deck, + struct cards_on_table *cot, char *trump_suit) +{ + int i; + + while(deck) { + for(i = 0; i <= cot->card_arr_idx; ++i) + if(((i == 0 || !(i % 3)) && cot->card_arr[i+2][0] == '-') && + is_card_beaten(cot->card_arr[i], deck->str, trump_suit)) + { + deck->is_usable = 1; + break; + } + deck = deck->next; + } +} + +void mark_card_for_tossing_stack(player_cards deck, struct cards_on_table *cot) +{ + int i; + + while(deck) { + for(i = 0; i <= cot->card_arr_idx; ++i) { + if(cot->card_arr[i][0] == '-' || cot->card_arr[i][0] == '\\') + continue; + if(check_matched_ranks(cot->card_arr[i], deck->str)) { + deck->is_usable = 1; + break; + } + } + deck = deck->next; + } +} + +char* card_search_by_marked_letter(player_cards deck, int letter) +{ + while(deck) { + if(deck->tip == letter && deck->is_usable) { + deck->is_usable = 0; + return deck->str; + } + deck = deck->next; + } + return NULL; +} + +char* card_search_by_unmarked_letter(player_cards deck, int letter) +{ + while(deck) { + if(deck->tip == letter) + return deck->str; + deck = deck->next; + } + return NULL; +} diff --git a/windows_client/card_stack.h b/windows_client/card_stack.h new file mode 100644 index 0000000..0daf40a --- /dev/null +++ b/windows_client/card_stack.h @@ -0,0 +1,30 @@ +#ifndef CARD_STACK_H_SENTRY +#define CARD_STACK_H_SENTRY + +struct card_stack_item { + char *str; + struct card_stack_item *next; + /* like a, b, c etc */ + unsigned char tip; + /* 1 - can be used, 0 - can't take it */ + int is_usable; +}; + +typedef struct card_stack_item* player_cards; + +void init_stack(player_cards *deck); +void push_stack(player_cards *deck, char *str); +void clear_stack(player_cards *deck); +int is_empty_stack(player_cards deck); +void add_hint_letters_stack(player_cards deck); +void mark_card_for_attackers_stack(player_cards deck); + +struct cards_on_table; +void mark_card_for_defenders_stack(player_cards deck, + struct cards_on_table *cot, + char* trump_suit); +void mark_card_for_tossing_stack(player_cards deck, struct cards_on_table *cot); +char* card_search_by_marked_letter(player_cards deck, int letter); +char* card_search_by_unmarked_letter(player_cards deck, int letter); + +#endif diff --git a/windows_client/client.c b/windows_client/client.c new file mode 100644 index 0000000..521dd0f --- /dev/null +++ b/windows_client/client.c @@ -0,0 +1,440 @@ +#include +#include +#include /* _getch() */ +#include +#include +#include +#include /* for debug */ + +#include "client.h" +#include "data_decryption.h" +#include "printing_game_frames.h" +#include "verification_client_input.h" + +static const char *ip = "109.107.161.30"; +static const int port = 1025; + +static void clean_up_resources(struct client *cl) +{ + free(cl->cc.number_arr); + clear_stack(&cl->deck); +} + +static void get_data_from_server(struct client *cl, fd_set *readfds) +{ + int i; + int update_info = 0; + /* pointer to delimeter (:) that is behind extract data */ + char *end_p; + + if(FD_ISSET(cl->fd, readfds) && !cl->read_data) { + if(!cl->data_left_in_buffer) { + cl->data_left_in_buffer = + recv(cl->fd, cl->buffer, max_buffer_size, 0); + /* end of file -- closed connection (from server) */ + if(!cl->data_left_in_buffer) { + clean_up_resources(cl); + WSACleanup(); + exit(0); + } +#ifdef DEBUG + printf("%d\n", cl->data_left_in_buffer); + for(i = 0; i < cl->data_left_in_buffer; ++i) + putchar(cl->buffer[i]); + putchar('\n'); +#endif + } + decrypt_set_state(cl, &end_p); + update_info = 1; + cl->display_new_frame = 1; + cl->pending_server_response = 0; + } + if(update_info) { + switch(cl->state) { + case first_player: + break; + case confirmation_waiting: + cl->total_players = decrypt_get_number(end_p+1, &end_p); + break; + case display_only_table: + case tossing_expectation: + case tossing: + /* last arg will contain pointer to '\n' */ + decrypt_set_base_info(cl, end_p+1, &end_p); + break; + case attack: + case defense: + case attack_expectation: + decrypt_set_base_info(cl, end_p+1, &end_p); + decrypt_set_position_whose_turn(cl, end_p+1, &end_p); + break; + case defense_expectation: + case spectator: + decrypt_set_base_info(cl, end_p+1, &end_p); + decrypt_set_position_whose_turn(cl, end_p+1, &end_p); + decrypt_set_card_queue(cl, end_p+1, &end_p); + if(cl->state == spectator) + decrypt_set_spectator_mode(cl, end_p+1); + break; + case card_acceptance_status: + decrypt_set_card_acceptance_status(cl, end_p+1); + break; + case result: + decrypt_set_durak_position(cl, end_p+1); + break; + /* no data to extract */ + case tossing_limit_status: + case disconnect: + default: + {} + } + for(i = 0; cl->buffer[i] != '\n'; ++i) + {} + if((cl->buffer+i - cl->buffer + 1) != cl->data_left_in_buffer) { + cl->data_left_in_buffer -= i + 1; + memmove(cl->buffer, cl->buffer+i+1, cl->data_left_in_buffer); + } else + cl->data_left_in_buffer = 0; + } +} + +static void prepare_tips_for_client(struct client *cl) +{ + char *trump_suit = NULL; + + if(!is_empty_stack(cl->deck)) + add_hint_letters_stack(cl->deck); + switch(cl->state) { + case attack: + mark_card_for_attackers_stack(cl->deck); + break; + case defense: + trump_suit = cl->trump_card + strlen(cl->trump_card) - 1; + mark_card_for_defenders_stack(cl->deck, &cl->cot, trump_suit); + break; + case tossing: + mark_card_for_tossing_stack(cl->deck, &cl->cot); + break; + default: + {} + } +} + +static void change_client_frame(struct client *cl) +{ + if(cl->display_new_frame) { + pgf_new_frame(); + switch(cl->state) { + case first_player: + pgf_first_player(); + break; + case confirmation_waiting: + pgf_confirmation_waiting(cl->total_players); + break; + case display_only_table: + case attack_expectation: + case defense_expectation: + case tossing_expectation: + pgf_table(cl); + if(cl->state == attack_expectation || + cl->state == defense_expectation || + cl->state == tossing_expectation) + { + pgf_select_idle_mode_message(cl->state); + } + break; + case spectator: + pgf_table(cl); + pgf_spectator_mode(cl->sp_mode); + break; + case attack: + case defense: + case tossing: + pgf_table(cl); + pgf_suggestions(cl); + break; + case card_acceptance_status: + pgf_card_acceptance_status(cl->all_input_cards_accepted); + break; + case tossing_limit_status: + pgf_tossing_limit_status(); + break; + case result: + pgf_game_result(cl->durak_position); + break; + case disconnect: + pgf_disconnect(); + break; + case none: + {} + } + cl->display_new_frame = 0; + } +} + +static int check_users_exit(enum client_states state, const char *buffer, + int size) +{ + if((state == first_player || state == confirmation_waiting) && + size == 2 && (buffer[0] == 'q' && buffer[1] == '\n')) + return 1; + + return size == 3 && !strncmp(buffer, "qq", 2) && buffer[2] == '\n'; +} + +static int fill_in_buffer(struct client *cl, int *data_size, + INPUT_RECORD *irInBuf) +{ + int i, buffer_idx, end_status = 0, input_status = 0; + int key; + + for(i = 0, buffer_idx = 0; i < *data_size; ++i) { + if(irInBuf[i].Event.KeyEvent.bKeyDown) { + key = irInBuf[i].Event.KeyEvent.uChar.AsciiChar; + if(key != '\n' && key != '\r' && (key < 'A' || key > 'z' )) + { + return 0; + } + cl->buffer[buffer_idx] = key; + putchar(key); + if(key == '\n' || key == '\r') { + cl->buffer[buffer_idx] = '\n'; + end_status = 1; + } + ++buffer_idx; + input_status = 1; + } + } + /* no data was input */ + if(!input_status) + return 0; + /* the data has not yet been entered up to the newline character */ + if(!end_status && input_status) { + for(; (key = _getch()) != '\r'; ) { + if(key == back && buffer_idx > 0) { + --buffer_idx; + cl->buffer[buffer_idx] = '\0'; + putchar(back); + putchar(' '); + putchar(back); + continue; + } else if(key == back) + continue; + cl->buffer[buffer_idx] = key; + ++buffer_idx; + putchar(key); + } + cl->buffer[buffer_idx] = '\n'; + putchar('\n'); + ++buffer_idx; /* like size of buffer */ + } + *data_size = buffer_idx; + return 1; +} + +static void send_data_to_server(struct client *cl) +{ + DWORD cNumRead; + int input_result = 0, data_size; + INPUT_RECORD irInBuf[128]; + + if(cl->read_data) { + ReadConsoleInput(GetStdHandle(STD_INPUT_HANDLE), irInBuf, 128, + &cNumRead); + data_size = (int) cNumRead; + /* data_size will be changed */ + if(!fill_in_buffer(cl, &data_size, irInBuf)) { + cl->read_data = 0; + return; + } + if(check_users_exit(cl->state, cl->buffer, data_size)) { + closesocket(cl->fd); + clean_up_resources(cl); + pgf_disconnect(); + WSACleanup(); + exit(0); + } + + switch(cl->state) { + case confirmation_waiting: + input_result = vci_confirmation_waiting(cl->fd, cl->buffer); + break; + case attack: + case tossing: + input_result = vci_attack_or_tossing(cl->fd, cl->buffer, cl->deck, + cl->state); + break; + case defense: + input_result = vci_defense(cl->fd, cl->buffer, cl->deck); + break; + default: + {} + } + /* if 0, then re-tracking the input client */ + if(input_result) + cl->pending_server_response = 1; + cl->read_data = 0; + } +} + +static int check_tracking_client_input(enum client_states state, + int pending_server_response) +{ + if(pending_server_response) + return 0; + + switch(state) { + case first_player: + case confirmation_waiting: + case attack: + case defense: + case tossing: + return 1; + default: + return 0; + } + return 0; +} + +static int key_was_pressed() +{ + return + WaitForSingleObject(GetStdHandle(STD_INPUT_HANDLE), 0) == WAIT_OBJECT_0; +} + +static void wait_for_key_press() +{ + WaitForSingleObject(GetStdHandle(STD_INPUT_HANDLE), INFINITE); +} + +int main_loop(struct client *cl) +{ + struct timeval timeout; + int select_result, error; + fd_set readfds; + DWORD fdwMode; + + fdwMode = + ENABLE_WINDOW_INPUT | ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | + ENABLE_PROCESSED_INPUT; + FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE)); + SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), fdwMode); + + for(;;) { + FD_ZERO(&readfds); + + FD_SET(cl->fd, &readfds); + if(check_tracking_client_input(cl->state, cl->pending_server_response)) { + if(cl->state == attack || cl->state == defense || + cl->state == tossing) + { + wait_for_key_press(); + if(key_was_pressed()) + cl->read_data = 1; + } else if(key_was_pressed()) { + cl->read_data = 1; + } + } + + if(!cl->data_left_in_buffer && !cl->read_data) { + if(cl->state == first_player || cl->state == confirmation_waiting) { + timeout.tv_sec = 1; + timeout.tv_usec = 0; + } + select_result = + select(cl->fd + 1, &readfds, NULL, NULL, + cl->state == first_player || + cl->state == confirmation_waiting ? &timeout : NULL); + if(select_result == SOCKET_ERROR) { + error = WSAGetLastError(); + if(error == WSANOTINITIALISED) + printf("WSANOTINITIALISED\n"); + else if(error == WSAEFAULT) + printf("WSAEFAULT\n"); + else if(error == WSAENETDOWN) + printf("WSAENETDOWN\n"); + else if(error == WSAEINVAL) + printf("WSAEINVAL\n"); + else if(error == WSAEINTR) + printf("WSAEINTR\n"); + else if(error == WSAEINPROGRESS) + printf("WSAEINPROGRESS\n"); + else if(error == WSAENOTSOCK) + printf("WSAENOTSOCK\n"); + return 2; + } + } + get_data_from_server(cl, &readfds); + prepare_tips_for_client(cl); + if(cl->display_new_frame) + change_client_frame(cl); + send_data_to_server(cl); + } +} + +static void init_client(struct client *cl) +{ + cl->fd = -1; + cl->state = none; + cl->total_players = 0; + cl->total_cards_left = 0; + cl->player_position = 0; + cl->cc.number_arr = NULL; + cl->cc.number_arr_idx = -1; + cl->cot.card_arr_idx = -1; + cl->deck = NULL; + cl->pending_server_response = 0; + cl->cq.card_arr_idx = -1; + cl->position_whose_turn = 0; + cl->display_new_frame = 0; + cl->all_input_cards_accepted = 0; + cl->data_left_in_buffer = 0; + cl->sp_mode = spectator_table; + cl->durak_position = 0; + cl->read_data = 0; +} + +/* + * 0 - failure + * 1 - success + */ +static int connect_to_server(struct client *cl) +{ + int connect_result; + struct sockaddr_in addr; + + cl->fd = socket(AF_INET, SOCK_STREAM, 0); + + if(cl->fd == -1) + return 0; + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = inet_addr(ip); + addr.sin_port = htons(port); + + connect_result = connect(cl->fd, (struct sockaddr*) &addr, sizeof(addr)); + if(connect_result == -1) + return 0; + return 1; +} + +int main() +{ + struct client cl; + static WSADATA wsaData; + int wsaerr = WSAStartup(MAKEWORD(2, 0), &wsaData); + if (wsaerr) + exit(1); + + pgf_new_frame(); + pgf_welcome(); + sleep(2); + init_client(&cl); + if(!connect_to_server(&cl)) { + pgf_new_frame(); + pgf_connection(0); + return 1; + } + pgf_new_frame(); + pgf_connection(1); + return main_loop(&cl); +} diff --git a/windows_client/client.h b/windows_client/client.h new file mode 100644 index 0000000..f3b1ce8 --- /dev/null +++ b/windows_client/client.h @@ -0,0 +1,87 @@ +#ifndef CLIENT_H_SENTRY +#define CLIENT_H_SENTRY + +#include "card_stack.h" + +enum { + max_buffer_size = 4096, + max_cot_arr_size = 18, + card_size = 4, + max_cq_arr_size = 5, + backspace = 127, + back = 8 +}; + +enum client_states { + none, + first_player, + confirmation_waiting, + /* in game */ + display_only_table, + attack_expectation, + defense_expectation, + tossing_expectation, + attack, + defense, + tossing, + card_acceptance_status, + tossing_limit_status, + spectator, + result, + disconnect +}; + +enum spectator_mode { + spectator_attack, + spectator_defense, + spectator_tossing, + spectator_table +}; + +struct cards_on_table { + /* + * example: 2^ \ - 10# \ K# + * [2 ^ '\0' ][1 0 # '\0'][\ '\0' ] + */ + char card_arr[max_cot_arr_size][card_size]; + /* index of the last filled element */ + int card_arr_idx; +}; + +struct card_queue { + char card_arr[max_cq_arr_size][card_size]; + int card_arr_idx; +}; + +struct card_count { + int *number_arr; + /* + * will only store the initial number of players + * (idx: total_players - 1 + */ + int number_arr_idx; +}; + +struct client { + int fd; + enum client_states state; + char buffer[max_buffer_size]; + int total_players; + int total_cards_left; + int player_position; + char trump_card[4]; + struct card_count cc; + struct cards_on_table cot; + player_cards deck; + int pending_server_response; + struct card_queue cq; + int position_whose_turn; + int display_new_frame; + int all_input_cards_accepted; + int data_left_in_buffer; + enum spectator_mode sp_mode; + int durak_position; + int read_data; +}; + +#endif diff --git a/windows_client/data_decryption.c b/windows_client/data_decryption.c new file mode 100644 index 0000000..1054c45 --- /dev/null +++ b/windows_client/data_decryption.c @@ -0,0 +1,181 @@ +#include "data_decryption.h" +#include "client.h" +#include "card_stack.h" + +#include +#include +#include + +void decrypt_set_state(struct client *cl, char **end_p) +{ + enum client_states found_state; + + found_state = (enum client_states) strtol(cl->buffer, end_p, 10); + cl->state = found_state; +} + +/* return convert number from str */ +int decrypt_get_number(char *start_p, char **end_p) +{ + return strtol(start_p, end_p, 10); +} + +static void decrypt_set_trump_card(struct client *cl, char *start_p, + char **end_p) +{ + int i; + + for(i = 0; *start_p != ':'; ++start_p, ++i) + cl->trump_card[i] = *start_p; + cl->trump_card[i] = '\0'; + *end_p = start_p; +} + +void decrypt_set_base_info(struct client *cl, char *start_p, char **end_p) +{ + int i, j; + char tmp_card[4]; + char *card = NULL; + + cl->total_players = decrypt_get_number(start_p, end_p); + cl->total_cards_left = decrypt_get_number(*end_p+1, end_p); + cl->player_position = decrypt_get_number(*end_p+1, end_p); + decrypt_set_trump_card(cl, *end_p+1, end_p); + + if(!cl->cc.number_arr) { + cl->cc.number_arr = malloc(cl->total_players * sizeof(int)); + /* + * memorize the size of the array, + * because later the number of players may change (client disconnection) + */ + cl->cc.number_arr_idx = cl->total_players-1; + } + + /* ============== extraction quantity of cards =============== */ + + /* numbers are separated by '\' */ + i = 0; + do { + cl->cc.number_arr[i] = decrypt_get_number(*end_p+1, end_p); + ++i; + } while(**end_p != ':'); + + /* ============== extraction cards on table =============== */ + i = 0, j = 0; + for(start_p = *end_p + 1; *start_p != ':'; ++start_p) { + /* empty table */ + if(*start_p == '=') { + /* to delimiter : */ + ++start_p; + i = -1; + break; + } + /* delimiter between cards on table */ + if(*start_p == '+') { + cl->cot.card_arr[i][j] = '\0'; + ++i; + j = 0; + continue; + } + cl->cot.card_arr[i][j] = *start_p; + ++j; + } + /* for the last card we need to add a null character */ + if(i != -1) + cl->cot.card_arr[i][j] = '\0'; + cl->cot.card_arr_idx = i; + + /* ================= extraction player's deck ================*/ + /* clear previous stack */ + if(cl->deck) + clear_stack(&cl->deck); + init_stack(&cl->deck); + + for(++start_p, i = 0; *start_p != ':' && *start_p != '\n'; ++start_p) { + /* empty deck */ + if(*start_p == '=') { + i = -1; + /* to delimiter ':' or '\n' */ + ++start_p; + break; + } + /* delimiter */ + if(*start_p == '\\') { + tmp_card[i] = '\0'; + card = malloc(strlen(tmp_card) + 1); + strcpy(card, tmp_card); + push_stack(&cl->deck, card); + i = 0; + continue; + } + tmp_card[i] = *start_p; + ++i; + } + if(i != -1) { + tmp_card[i] = '\0'; + card = malloc(strlen(tmp_card) + 1); + strcpy(card, tmp_card); + push_stack(&cl->deck, card); + } + *end_p = start_p; +} + +void decrypt_set_position_whose_turn(struct client *cl, char *start_p, + char **end_p) +{ + cl->position_whose_turn = decrypt_get_number(start_p, end_p); +} + +void decrypt_set_card_queue(struct client *cl, char *start_p, char **end_p) +{ + int i, j; + + for(i = 0, j = 0; *start_p != ':' && *start_p != '\n'; ++start_p) { + /* empty queue */ + if(*start_p == '=') { + i = -1; + ++start_p; + break; + } + /* delimiter '\' */ + if(*start_p == '\\') { + cl->cq.card_arr[i][j] = '\0'; + j = 0; + ++i; + continue; + } + cl->cq.card_arr[i][j] = *start_p; + ++j; + } + /* last element */ + if(i != -1) + cl->cq.card_arr[i][j] = '\0'; + cl->cq.card_arr_idx = i; + *end_p = start_p; +} + +void decrypt_set_card_acceptance_status(struct client *cl, char *start_p) +{ + int i; + char tmp_buffer[8]; + + for(i = 0; *start_p != '\n'; ++start_p, ++i) + tmp_buffer[i] = *start_p; + tmp_buffer[i] = '\0'; + if(!strcmp(tmp_buffer, "all")) { + cl->all_input_cards_accepted = 1; + return; + } + if(!strcmp(tmp_buffer, "not all")) + cl->all_input_cards_accepted = 0; +} + +void decrypt_set_spectator_mode(struct client *cl, char *start_p) +{ + cl->sp_mode = (enum spectator_mode) decrypt_get_number(start_p, NULL); +} + +void decrypt_set_durak_position(struct client *cl, char *start_p) +{ + cl->durak_position = decrypt_get_number(start_p, NULL); +} diff --git a/windows_client/data_decryption.h b/windows_client/data_decryption.h new file mode 100644 index 0000000..08772fe --- /dev/null +++ b/windows_client/data_decryption.h @@ -0,0 +1,16 @@ +#ifndef DATA_DECRYPTION_H_SENTRY +#define DATA_DECRYPTION_H_SENTRY + +#include "client.h" + +void decrypt_set_state(struct client *cl, char **end_p); +int decrypt_get_number(char *start_p, char **end_p); +void decrypt_set_base_info(struct client *cl, char *start_p, char **end_p); +void decrypt_set_position_whose_turn(struct client *cl, char *start_p, + char **end_p); +void decrypt_set_card_queue(struct client *cl, char *start_p, char **end_p); +void decrypt_set_card_acceptance_status(struct client *cl, char *start_p); +void decrypt_set_spectator_mode(struct client *cl, char *start_p); +void decrypt_set_durak_position(struct client *cl, char *start_p); + +#endif diff --git a/windows_client/printing_game_frames.c b/windows_client/printing_game_frames.c new file mode 100644 index 0000000..6b2131a --- /dev/null +++ b/windows_client/printing_game_frames.c @@ -0,0 +1,183 @@ +#include "printing_game_frames.h" +#include "card_stack.h" + +#include +#include + +void pgf_new_frame() +{ + int i; + + for(i = 0; i < 25; ++i) + printf("\n"); +} + +void pgf_welcome() +{ + printf("======================================================\n" + " Welcome!\n" + "You're playing siege durak/Podkidnoy (Throw-in) durak\n\n" + "If you have any questions, you can contact by email to\n" + " m@scratko.xyz\n" + "======================================================\n"); +} + +void pgf_connection(int status) +{ + if(status) + printf("Connection to the server has been successfully established\n"); + else + printf("Sorry, failed to connect to the server\n"); +} + +void pgf_first_player() +{ + printf("=======================================\n" + " You're the first player\n" + "Waiting for other players to connect...\n" + " Press q to exit\n" + "=======================================\n"); +} + +void pgf_confirmation_waiting(int total_players) +{ + printf("=======================================\n" + " To start game press key\n" + " Connected players: %u/8\n" + " Press q to exit\n" + "=======================================\n", total_players); +} + +static int move_indication_condition(const struct client *cl, + int current_player) +{ + return + current_player == cl->position_whose_turn && + (cl->state == attack_expectation || cl->state == defense_expectation || + cl->state == attack || cl->state == defense || + (cl->state == spectator && (cl->sp_mode == spectator_attack || + cl->sp_mode == spectator_defense))); +} + +void pgf_table(struct client *cl) +{ + struct card_stack_item *deck = NULL; + int i; + + for(i = 0; i <= cl->cc.number_arr_idx; ++i) + printf("<%s %u %c> ", cl->player_position == i+1 ? "YOU" : " ", + cl->cc.number_arr[i], move_indication_condition(cl, i+1) ? + '*' : ' '); + printf(" %s [ %u ]", cl->trump_card, cl->total_cards_left); + printf("\n\n\n"); + /* ================= cards on table ================ */ + for(i = 0; i <= cl->cot.card_arr_idx; i += 3) + printf(" %s %s %s\n", cl->cot.card_arr[i], cl->cot.card_arr[i+1], + cl->cot.card_arr[i+2]); + /* for the defender, the cards in the queue are hidden */ + if(cl->state == defense_expectation || cl->state == spectator) + for(i = 0; i <= cl->cq.card_arr_idx; ++i) + printf(" %s \\ -\n", cl->cq.card_arr[i]); + if(cl->cot.card_arr_idx != -1) + printf("\n\n"); + /* ================ player's deck ================ */ + deck = cl->deck; + i = 0; + while(deck) { + printf("%c: %s ", deck->tip, deck->str); + ++i; + /* line break every 6 cards */ + if(!(i % 6)) + printf("\n"); + deck = deck->next; + } + if(cl->deck) + printf("\n"); +} + +void pgf_suggestions(struct client *cl) +{ + struct card_stack_item *deck = NULL; + + printf("qq - exit the game\n"); + if(cl->state == attack || cl->state == tossing) + printf("you can specify more than one card\n"); + deck = cl->deck; + while(deck) { + if(deck->is_usable) + printf("%c", deck->tip); + deck = deck->next; + } + printf(" > "); + fflush(stdout); +} + +void pgf_select_idle_mode_message(enum client_states state) +{ + switch(state) { + case attack_expectation: + printf("waiting for the attacker's turn\n"); + break; + case defense_expectation: + printf("waiting for the defender's turn\n"); + break; + case tossing_expectation: + printf("waiting for other players to toss cards\n"); + break; + default: + {} + } +} + +void pgf_card_acceptance_status(int all_input_cards_accepted) +{ + if(all_input_cards_accepted) + printf("all cards accepted\n"); + else + printf("not all cards were accepted\n"); +} + +void pgf_tossing_limit_status() +{ + printf("the cards were not accepted.\n" + "The other players have already tossed cards.\n"); +} + +void pgf_spectator_mode(enum spectator_mode sp_mode) +{ + printf("spectator mode\n"); + if(sp_mode == spectator_attack) + printf("attacker makes a move\n"); + else if(sp_mode == spectator_defense) + printf("defender make a move\n"); + else if(sp_mode == spectator_tossing) + printf("players toss in extra cards\n"); + else + printf("round result\n"); +} + +void pgf_game_result(int durak_position) +{ + printf("=======================================\n" + " END OF GAME\n" + " player number %u is durak\n" + "=======================================\n", durak_position); +} + +static void clear_input() +{ + int key; + + while((key = getchar()) != '\n') + {} +} + +void pgf_disconnect() +{ + char key; + + printf("Server connection disconnected\n" + "Press to close program\n"); + while((key = getchar()) != '\n') + clear_input(); +} diff --git a/windows_client/printing_game_frames.h b/windows_client/printing_game_frames.h new file mode 100644 index 0000000..9cbb231 --- /dev/null +++ b/windows_client/printing_game_frames.h @@ -0,0 +1,20 @@ +#ifndef PRINTING_GAME_FRAMES_H_SENTRY +#define PRINTING_GAME_FRAMES_H_SENTRY + +#include "client.h" + +void pgf_new_frame(); +void pgf_welcome(); +void pgf_connection(int status); +void pgf_first_player(); +void pgf_confirmation_waiting(int total_players); +void pgf_table(struct client *cl); +void pgf_suggestions(struct client *cl); +void pgf_select_idle_mode_message(enum client_states state); +void pgf_card_acceptance_status(int all_input_cards_accepted); +void pgf_tossing_limit_status(); +void pgf_spectator_mode(enum spectator_mode sp_mode); +void pgf_game_result(int durak_position); +void pgf_disconnect(); + +#endif diff --git a/windows_client/verification_client_input.c b/windows_client/verification_client_input.c new file mode 100644 index 0000000..08b00fc --- /dev/null +++ b/windows_client/verification_client_input.c @@ -0,0 +1,116 @@ +#include "verification_client_input.h" +#include "card_handling.h" +#include "printing_game_frames.h" + +#include +#include +#include +#include + +enum { + output_buffer_size = 1024 +}; + +static char output_buffer[output_buffer_size]; + +/* + * 1 - response received + * 0 - waiting for re-entry of data + */ +int vci_confirmation_waiting(int fd, char *buffer) +{ + if(buffer[0] == '\n') { + send(fd, buffer, 1, 0); + return 1; + } + else + printf("please press enter\n"); + return 0; +} + +/* + * example: A#\A^\Av\'\n' + * or (while tossing): '\n' + */ +int vci_attack_or_tossing(int fd, char *buffer, player_cards deck, + enum client_states state) +{ + int rank = 0, i, free_pos = 0; + char * card = NULL; + + if(state == tossing && buffer[0] == '\n') { /* cancel card tossing */ + send(fd, buffer, 1, 0); + printf("skipping the card toss\n"); + return 1; + } + + for(i = 0; buffer[i] != '\n'; ++i) { + /* some symbols */ + if(state == attack && buffer[1] != '\n') { + card = card_search_by_marked_letter(deck, buffer[i]); + if(card) { + if(!rank) + rank = convert_rank_to_int(card); + if(rank != convert_rank_to_int(card)) { + printf("incorrect input\n> "); + fflush(stdout); + return 0; + } + } + /* one symbol inputed? */ + } else if(state == attack && buffer[1] == '\n') + card = card_search_by_unmarked_letter(deck, buffer[i]); + else if(state == tossing) + card = card_search_by_marked_letter(deck, buffer[i]); + + if(!card) { + printf("incorrect input\n> "); + fflush(stdout); + return 0; + } + strncpy(output_buffer + free_pos, card, strlen(card)); + free_pos += strlen(card); + output_buffer[free_pos] = '\\'; + ++free_pos; + } + if(state == attack && buffer[0] == '\n') { + printf("incorrect input\n> "); + fflush(stdout); + return 0; + } + output_buffer[free_pos] = '\n'; + send(fd, output_buffer, free_pos+1, 0); + return 1; +} + +/* + * example: 5v'\n' + * or: '\n' + */ +int vci_defense(int fd, char *buffer, player_cards deck) +{ + int last_idx; + char *card = NULL; + + if(buffer[0] == '\n') { /* the player does not want to keep the defense */ + send(fd, buffer, 1, 0); + printf("accepting cards\n"); + return 1; + } + if(buffer[1] != '\n') { + printf("incorrect input\n> "); + fflush(stdout); + return 0; + } + if((card = card_search_by_marked_letter(deck, buffer[0])) != NULL) { + strcpy(output_buffer, card); + last_idx = strlen(card); + output_buffer[last_idx] = '\n'; + send(fd, output_buffer, last_idx+1, 0); + return 1; + } + /* card is not found in the deck or cannot be used */ + printf("incorrect input\n> "); + fflush(stdout); + return 0; +} diff --git a/windows_client/verification_client_input.h b/windows_client/verification_client_input.h new file mode 100644 index 0000000..690f36e --- /dev/null +++ b/windows_client/verification_client_input.h @@ -0,0 +1,12 @@ +#ifndef VERIFICATION_CLIENT_INPUT_H_SENTRY +#define VERIFICATION_CLIENT_INPUT_H_SENTRY + +#include "card_stack.h" +#include "client.h" + +int vci_confirmation_waiting(int fd, char *buffer); +int vci_attack_or_tossing(int fd, char *buffer, player_cards deck, + enum client_states state); +int vci_defense(int fd, char *buffer, player_cards deck); + +#endif -- cgit v1.2.3