back to scratko.xyz
aboutsummaryrefslogtreecommitdiff
path: root/linux_client
diff options
context:
space:
mode:
authorscratko <m@scratko.xyz>2024-08-30 12:46:56 +0300
committerscratko <m@scratko.xyz>2024-08-30 14:59:44 +0300
commit831f9f01fbe4088eb6bd378c0e417d9996b676fd (patch)
tree53297459d35ad795618c351a79b1829776e5e1f4 /linux_client
parent4b6c15f780d59895f067383a5041edcfe86f504e (diff)
downloaddurak-831f9f01fbe4088eb6bd378c0e417d9996b676fd.tar.gz
durak-831f9f01fbe4088eb6bd378c0e417d9996b676fd.tar.bz2
durak-831f9f01fbe4088eb6bd378c0e417d9996b676fd.zip
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.
Diffstat (limited to 'linux_client')
-rw-r--r--linux_client/Makefile23
-rw-r--r--linux_client/card_handling.c64
-rw-r--r--linux_client/card_handling.h9
-rw-r--r--linux_client/card_stack.c173
-rw-r--r--linux_client/card_stack.h30
-rw-r--r--linux_client/client.c321
-rw-r--r--linux_client/client.h84
-rw-r--r--linux_client/data_decryption.c181
-rw-r--r--linux_client/data_decryption.h16
-rw-r--r--linux_client/printing_game_frames.c183
-rw-r--r--linux_client/printing_game_frames.h20
-rw-r--r--linux_client/verification_client_input.c115
-rw-r--r--linux_client/verification_client_input.h12
13 files changed, 1231 insertions, 0 deletions
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 <stdlib.h>
+#include <string.h>
+
+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 <stdlib.h>
+#include <string.h>
+
+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 <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h> /* 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 <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+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 <stdio.h>
+#include <unistd.h>
+
+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 <Enter> to exit\n"
+ "=======================================\n");
+}
+
+void pgf_confirmation_waiting(int total_players)
+{
+ printf("=======================================\n"
+ " To start game press <Enter> key\n"
+ " Connected players: %u/8\n"
+ " Press q <Enter> 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 <Enter> 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 <stdio.h>
+#include <unistd.h>
+#include <string.h>
+
+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