back to scratko.xyz
aboutsummaryrefslogtreecommitdiff
path: root/server
diff options
context:
space:
mode:
authorscratko <m@scratko.xyz>2024-08-10 02:46:56 +0300
committerscratko <m@scratko.xyz>2024-08-10 02:46:56 +0300
commita2d696dea797faaa3157046c8ae89cd70e965bff (patch)
tree74051e828ec401f399b2316a535c200f3afa95c5 /server
parent9e9919b897b00ff23aee6581471b4d7b4567bf4a (diff)
downloaddurak-a2d696dea797faaa3157046c8ae89cd70e965bff.tar.gz
durak-a2d696dea797faaa3157046c8ae89cd70e965bff.tar.bz2
durak-a2d696dea797faaa3157046c8ae89cd70e965bff.zip
Prefinal version
Added client. Moved files to directories.
Diffstat (limited to 'server')
-rw-r--r--server/Makefile17
-rw-r--r--server/card_queue.c61
-rw-r--r--server/card_queue.h22
-rw-r--r--server/card_stack.c79
-rw-r--r--server/card_stack.h21
-rw-r--r--server/server.c759
-rw-r--r--server/server.h99
-rw-r--r--server/server_data_processing.c337
-rw-r--r--server/server_data_processing.h21
-rw-r--r--server/server_game_process.c475
-rw-r--r--server/server_game_process.h78
11 files changed, 1969 insertions, 0 deletions
diff --git a/server/Makefile b/server/Makefile
new file mode 100644
index 0000000..6fb2c5c
--- /dev/null
+++ b/server/Makefile
@@ -0,0 +1,17 @@
+SRCMODULES = card_stack.c card_queue.c server_data_processing.c server_game_process.c server.c
+OBJMODULES = $(SRCMODULES:.c=.o)
+CC = gcc
+CFLAGS = -Wall -g -c
+
+all: server
+
+%.o: %.с %.h
+ $(CC) $(CFLAGS) $< -o $@
+
+server: $(OBJMODULES)
+ $(CC) $(LIBS) $^ -o $@
+
+-include deps.mk
+
+deps.mk: $(SRCMODULES)
+ $(CC) -MM $^ > $@
diff --git a/server/card_queue.c b/server/card_queue.c
new file mode 100644
index 0000000..194ea0c
--- /dev/null
+++ b/server/card_queue.c
@@ -0,0 +1,61 @@
+#include "card_queue.h"
+
+#include <stdlib.h>
+
+void init_queue(struct card_queue *cq)
+{
+ cq->first = NULL;
+ cq->last = NULL;
+}
+
+void push_queue(struct card_queue *cq, const char *str)
+{
+ struct card_queue_item *tmp = malloc(sizeof(struct card_queue_item));
+ tmp->str = str;
+ tmp->next = NULL;
+ if(!cq->first) {
+ cq->first = tmp;
+ cq->last = tmp;
+ } else {
+ cq->last->next = tmp;
+ cq->last = tmp;
+ }
+}
+
+struct card_queue_item* get_next_card_from_queue(struct card_queue *cq,
+ struct card_queue_item *prev)
+{
+ if(prev == NULL)
+ return cq->first;
+ else
+ return prev->next;
+}
+
+int is_empty_queue(struct card_queue *cq)
+{
+ return !cq->first;
+}
+
+const char* pop_card_queue(struct card_queue *cq)
+{
+ struct card_queue_item *tmp = NULL;
+ const char *card = NULL;
+
+ tmp = cq->first;
+ cq->first = cq->first->next;
+ card = tmp->str;
+ free(tmp);
+ return card;
+}
+
+int find_out_card_quantity_in_cq(const struct card_queue *cq)
+{
+ int counter = 0;
+ struct card_queue_item *tmp = cq->first;
+
+ while(tmp) {
+ ++counter;
+ tmp = tmp->next;
+ }
+ return counter;
+}
diff --git a/server/card_queue.h b/server/card_queue.h
new file mode 100644
index 0000000..778ece4
--- /dev/null
+++ b/server/card_queue.h
@@ -0,0 +1,22 @@
+#ifndef H_SENTRY_CARD_QUEUE
+#define H_SENTRY_CARD_QUEUE
+
+struct card_queue_item {
+ const char *str;
+ struct card_queue_item *next;
+};
+
+struct card_queue {
+ struct card_queue_item *first;
+ struct card_queue_item *last;
+};
+
+void init_queue(struct card_queue *cq);
+void push_queue(struct card_queue *cq, const char *str);
+struct card_queue_item* get_next_card_from_queue(struct card_queue *cq,
+ struct card_queue_item *prev);
+int find_out_card_quantity_in_cq(const struct card_queue *cq);
+int is_empty_queue(struct card_queue *cq);
+const char* pop_card_queue(struct card_queue *cq);
+
+#endif
diff --git a/server/card_stack.c b/server/card_stack.c
new file mode 100644
index 0000000..aa8af71
--- /dev/null
+++ b/server/card_stack.c
@@ -0,0 +1,79 @@
+#include "card_stack.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+void init_stack(player_cards *deck)
+{
+ *deck = NULL;
+}
+
+void push_stack(player_cards *deck, const char *str)
+{
+ struct card_stack_item *new_card = malloc(sizeof(struct card_stack_item));
+ new_card->str = str;
+ new_card->next = *deck;
+ if(new_card->next)
+ new_card->next->prev = new_card;
+ new_card->prev = NULL;
+ *deck = new_card;
+}
+
+int is_empty_stack(const player_cards deck)
+{
+ return !deck;
+}
+
+int find_out_card_quantity_in_deck(player_cards deck)
+{
+ int counter = 0;
+
+ while(deck) {
+ ++counter;
+ deck = deck->next;
+ }
+ return counter;
+}
+
+const char* remove_card_from_stack(player_cards *deck, const char *str)
+{
+ struct card_stack_item *tmp = NULL;
+ struct card_stack_item *first_tmp = *deck;
+ const char *card = NULL;
+
+ while(first_tmp) {
+ if(!strcmp(first_tmp->str, str)) {
+ tmp = first_tmp;
+ if(first_tmp->prev)
+ first_tmp->prev->next = first_tmp->next;
+ else
+ *deck = first_tmp->next;
+ if(first_tmp->next)
+ first_tmp->next->prev = first_tmp->prev;
+ card = tmp->str;
+ free(tmp);
+ return card;
+ }
+ first_tmp = first_tmp->next;
+ }
+ return 0;
+}
+
+struct card_stack_item* get_next_card_from_stack(player_cards deck,
+ player_cards prev)
+{
+ if(prev == NULL)
+ return deck;
+ else
+ return prev->next;
+}
+
+int find_card_in_stack(player_cards involved_cards, const char *str)
+{
+ while(involved_cards) {
+ if(!strcmp(involved_cards->str, str))
+ return 1;
+ involved_cards = involved_cards->next;
+ }
+ return 0;
+}
diff --git a/server/card_stack.h b/server/card_stack.h
new file mode 100644
index 0000000..d380ded
--- /dev/null
+++ b/server/card_stack.h
@@ -0,0 +1,21 @@
+#ifndef CARD_STACK_H_SENTRY
+#define CARD_STACK_H_SENTRY
+
+struct card_stack_item {
+ const char *str;
+ struct card_stack_item *next;
+ struct card_stack_item *prev;
+};
+
+typedef struct card_stack_item* player_cards;
+
+void init_stack(player_cards *deck);
+void push_stack(player_cards *deck, const char *str);
+int is_empty_stack(const player_cards deck);
+int find_out_card_quantity_in_deck(player_cards deck);
+const char* remove_card_from_stack(player_cards *deck, const char *str);
+struct card_stack_item* get_next_card_from_stack(player_cards deck,
+ player_cards prev);
+int find_card_in_stack(player_cards involved_cards, const char *str);
+
+#endif
diff --git a/server/server.c b/server/server.c
new file mode 100644
index 0000000..0a2bd0f
--- /dev/null
+++ b/server/server.c
@@ -0,0 +1,759 @@
+#include <stdio.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <time.h>
+
+#include "server.h"
+#include "server_data_processing.h"
+#include "server_game_process.h"
+#include "card_stack.h"
+#include "card_queue.h"
+
+
+static void init_session(struct session *new_session, struct sockaddr_in *from,
+ int fd, int player_position)
+{
+ new_session->fd = fd;
+ new_session->record = 1;
+ new_session->state = display_only_table;
+ new_session->player_position = player_position;
+ new_session->tm = none;
+ new_session->defense_lost = 0;
+ init_stack(&new_session->deck);
+}
+
+static int accept_client(struct server *serv)
+{
+ int fd, i;
+ struct sockaddr_in addr;
+ struct session *new_session;
+ socklen_t len = sizeof(addr);
+ fd = accept(serv->listen_socket, (struct sockaddr*) &addr, &len);
+ if(fd == -1)
+ return -1;
+
+ if(fd >= serv->max_sess_arr_size) {
+ int newlen = serv->max_sess_arr_size;
+ while(fd >= newlen)
+ newlen += init_sess_arr_size;
+ serv->sess_arr =
+ realloc(serv->sess_arr, newlen * sizeof(struct session*));
+ for(i = serv->max_sess_arr_size; i < newlen; i++)
+ serv->sess_arr[i] = NULL;
+ serv->max_sess_arr_size = newlen;
+ }
+ new_session = malloc(sizeof(struct session));
+ serv->sess_arr[fd] = new_session;
+ ++serv->connected_players_counter;
+ init_session(new_session, &addr, fd, serv->connected_players_counter);
+ return 1;
+}
+
+static int check_playable_player_number(struct server *serv)
+{
+ return serv->connected_players_counter >= 2 &&
+ serv->connected_players_counter < 8;
+}
+
+static int is_max_playable_player_number(struct server *serv)
+{
+ return serv->connected_players_counter == 8;
+}
+
+static void init_new_game(struct server *serv)
+{
+ int i,j, lowest_trump, current_trump, who_attack;
+ /* idx in serv->turn_queue */
+ who_attack = -1;
+ lowest_trump = 0;
+
+ serv->shuffled_deck = get_shuffled_deck();
+ serv->trump_card =
+ extract_trump_card(serv->shuffled_deck, &serv->shuffled_deck_size);
+ serv->trump_suit = serv->trump_card + strlen(serv->trump_card) - 1;
+#if 0
+ serv->trump_suit = find_trump_suit(serv->shuffled_deck,
+ &serv->shuffled_deck_size);
+#endif
+ serv->turn_queue = malloc(sizeof(struct session*) *
+ serv->connected_players_counter);
+ /* number of cards each player holds */
+ serv->cc.number_arr_idx = serv->connected_players_counter - 1;
+ serv->cc.number_arr = malloc(sizeof(int) * serv->connected_players_counter);
+ serv->position_whose_turn = 0;
+ serv->cot.card_arr_idx = -1;
+ /*
+ * dealing cards to all and
+ * determining who moves first
+ */
+ for(i = 0, j = 0; i < serv->max_sess_arr_size; ++i) {
+ if(serv->sess_arr[i]) {
+ serv->turn_queue[j] = serv->sess_arr[i];
+ deal_first_cards(serv->shuffled_deck, &serv->shuffled_deck_size,
+ &serv->sess_arr[i]->deck);
+ current_trump = find_lowest_trump(serv->sess_arr[i]->deck,
+ serv->trump_suit);
+ if(current_trump)
+ if(!lowest_trump || current_trump < lowest_trump) {
+ lowest_trump = current_trump;
+ who_attack = j;
+ }
+ ++j;
+ }
+ }
+ /* random selection of the first player */
+ if(who_attack == -1) {
+ who_attack =
+ 0 + (int)((double)serv->connected_players_counter*rand()/
+ (RAND_MAX + 1.0));
+ }
+ for(i = 0; i < who_attack; ++i)
+ shift_turn_queue_by_one(serv->turn_queue,
+ serv->connected_players_counter);
+ update_card_quantity_arr(serv);
+ init_queue(&serv->cq);
+ serv->gi = get_new_game_info(serv->connected_players_counter,
+ serv->cc.number_arr,
+ serv->shuffled_deck_size,
+ serv->trump_card, serv->position_whose_turn,
+ &serv->cq, &serv->cot);
+}
+
+static void set_whose_turn(struct server *serv, enum client_game_states state)
+{
+ switch(state) {
+ case attack:
+ serv->position_whose_turn = serv->turn_queue[0]->player_position;
+ break;
+ case defense:
+ serv->position_whose_turn = serv->turn_queue[1]->player_position;
+ break;
+ case tossing:
+ default:
+ serv->position_whose_turn = 0;
+ }
+}
+
+static void set_state_for_client(struct server *serv,
+ enum client_game_states state)
+{
+ int i;
+
+ switch(state) {
+ case attack:
+ serv->turn_queue[0]->state = attack;
+ break;
+ case defense:
+ serv->turn_queue[1]->state = defense;
+ break;
+ case tossing:
+ serv->turn_queue[1]->state = tossing_expectation;
+ default:
+ {}
+ }
+
+ for(i = 0; i < serv->connected_players_counter; ++i) {
+ switch(state) {
+ case attack:
+ if(serv->turn_queue[i] && serv->turn_queue[i] !=
+ serv->turn_queue[0])
+ serv->turn_queue[i]->state = attack_expectation;
+ break;
+ case defense:
+ if(serv->turn_queue[i] && serv->turn_queue[i] !=
+ serv->turn_queue[1])
+ serv->turn_queue[i]->state = defense_expectation;
+ break;
+ case tossing:
+ if(serv->turn_queue[i] && serv->turn_queue[i] !=
+ serv->turn_queue[1]) {
+ if(check_player_can_toss_card(serv->turn_queue[i]->deck,
+ &serv->cot))
+ {
+ serv->turn_queue[i]->tm = answer_wait;
+ serv->turn_queue[i]->state = tossing;
+ } else {
+ serv->turn_queue[i]->tm = none;
+ serv->turn_queue[i]->state = tossing_expectation;
+ }
+ }
+ break;
+ case display_only_table:
+ if(serv->turn_queue[i])
+ serv->turn_queue[i]->state = display_only_table;
+ break;
+ default:
+ {}
+ }
+ }
+}
+
+static void set_record_status_for_all(struct server *serv)
+{
+ int i;
+ for(i = 0; i < serv->max_sess_arr_size; ++i)
+ if(serv->sess_arr[i])
+ serv->sess_arr[i]->record = 1;
+}
+
+static int print_table_condition(struct server *serv)
+{
+ return !check_defender_can_defend(&serv->cq, serv->turn_queue[1]->deck,
+ serv->trump_suit) &&
+ !check_someone_can_toss_card(serv->turn_queue,
+ serv->connected_players_counter,
+ &serv->cq, &serv->cot);
+}
+
+static int defense_condition(struct server *serv)
+{
+ return check_defender_can_defend(&serv->cq, serv->turn_queue[1]->deck,
+ serv->trump_suit);
+}
+/*
+ * TODO refactoring
+ */
+static void define_phase_after_attack(struct server *serv)
+{
+ if(print_table_condition(serv)) {
+ set_state_for_client(serv, display_only_table);
+ put_all_cards_from_queue_to_table(&serv->cot, &serv->cq);
+ serv->turn_queue[1]->defense_lost = 1;
+ serv->state = table;
+ } else if(defense_condition(serv)) {
+ set_state_for_client(serv, defense);
+ set_whose_turn(serv, defense);
+ put_one_card_from_queue_to_table(&serv->cot, &serv->cq);
+ serv->state = defense_phase_out;
+ } else {
+ serv->turn_queue[1]->defense_lost = 1;
+ put_all_cards_from_queue_to_table(&serv->cot, &serv->cq);
+ if(is_receiving_cards_limit(&serv->cot, serv->turn_queue[1]->deck,
+ &serv->cq)) {
+ set_state_for_client(serv, display_only_table);
+ serv->state = table;
+ } else {
+ set_state_for_client(serv, tossing);
+ set_whose_turn(serv, tossing);
+ serv->state = tossing_phase_out;
+ }
+
+ }
+}
+
+static void define_phase_after_defense(struct server *serv)
+{
+ /* card wasn't beated */
+ if(serv->cot.card_arr[serv->cot.card_arr_idx][0] == '-') {
+ serv->turn_queue[1]->defense_lost = 1;
+ put_all_cards_from_queue_to_table(&serv->cot, &serv->cq);
+ /* go to tossing cards */
+ if(!is_receiving_cards_limit(&serv->cot, serv->turn_queue[1]->deck,
+ &serv->cq) &&
+ check_someone_can_toss_card(serv->turn_queue,
+ serv->connected_players_counter,
+ &serv->cq, &serv->cot))
+ {
+ set_state_for_client(serv, tossing);
+ set_whose_turn(serv, tossing);
+ serv->state = tossing_phase_out;
+ /* then begin new attack */
+ } else {
+ set_state_for_client(serv, display_only_table);
+ serv->state = table;
+ }
+ /* can defender continue defense? */
+ } else {
+ /* the defender has beaten the attack or tossing phase completely */
+ if(is_empty_queue(&serv->cq)) {
+ if(!is_receiving_cards_limit(&serv->cot, serv->turn_queue[0]->deck,
+ &serv->cq) &&
+ check_someone_can_toss_card(serv->turn_queue,
+ serv->connected_players_counter,
+ &serv->cq, &serv->cot))
+ {
+ set_state_for_client(serv, tossing);
+ set_whose_turn(serv, tossing);
+ serv->state = tossing_phase_out;
+ } else {
+ set_state_for_client(serv, display_only_table);
+ serv->state = table;
+ }
+ /* card queue contains some cards for defender */
+ } else {
+ /* defender can defense */
+ if(defense_condition(serv)) {
+ put_one_card_from_queue_to_table(&serv->cot, &serv->cq);
+ set_state_for_client(serv, defense);
+ set_whose_turn(serv, defense);
+ serv->state = defense_phase_out;
+ }
+ /* defender can't defense */
+#if 0
+ } else {
+ serv->turn_queue[1]->defense_lost = 1;
+ put_all_cards_from_queue_to_table(&serv->cot, &serv->cq);
+ if(check_someone_can_toss_card(serv->turn_queue,
+ serv->connected_players_counter,
+ &serv->cq, &serv->cot))
+ {
+ set_state_for_client(serv, tossing);
+ set_whose_turn(serv, tossing);
+ serv->state = tossing_phase_out;
+ } else {
+ move_all_cards_to_defender(&serv->cot,
+ &serv->turn_queue[1]->deck);
+ /* add shifting of turn arr*/
+ set_state_for_client(serv, attack);
+ set_whose_turn(serv, attack);
+ serv->state = attack_phase_out;
+ }
+ }
+#endif
+ }
+ }
+}
+
+static void define_phase_after_tossing(struct server *serv)
+{
+ if(serv->turn_queue[1]->defense_lost) {
+ if(is_empty_queue(&serv->cq)) {
+ /* nobody's put any cards on the table */
+ set_state_for_client(serv, display_only_table);
+ serv->state = table;
+ }
+ else {
+ put_all_cards_from_queue_to_table(&serv->cot, &serv->cq);
+ if(is_receiving_cards_limit(&serv->cot, serv->turn_queue[1]->deck,
+ &serv->cq) &&
+ check_someone_can_toss_card(serv->turn_queue,
+ serv->connected_players_counter,
+ &serv->cq, &serv->cot))
+ {
+ set_state_for_client(serv, tossing);
+ set_whose_turn(serv, tossing);
+ serv->state = tossing_phase_out;
+ } else {
+ set_state_for_client(serv, display_only_table);
+ serv->state = table;
+ }
+ }
+ /* defense continue? */
+ } else {
+ if(is_empty_queue(&serv->cq)) {
+ set_state_for_client(serv, display_only_table);
+ serv->state = table;
+ } else {
+ if(check_defender_can_defend(&serv->cq, serv->turn_queue[1]->deck,
+ serv->trump_suit)) {
+ put_one_card_from_queue_to_table(&serv->cot, &serv->cq);
+ set_state_for_client(serv, defense);
+ set_whose_turn(serv, defense);
+ serv->state = defense_phase_out;
+ } else {
+ serv->turn_queue[1]->defense_lost = 1;
+ put_all_cards_from_queue_to_table(&serv->cot, &serv->cq);
+ if(is_receiving_cards_limit(&serv->cot,
+ serv->turn_queue[1]->deck,
+ &serv->cq) &&
+ check_someone_can_toss_card(serv->turn_queue,
+ serv->connected_players_counter,
+ &serv->cq, &serv->cot))
+ {
+ set_state_for_client(serv, tossing);
+ set_whose_turn(serv, tossing);
+ serv->state = tossing_phase_out;
+ } else {
+ set_state_for_client(serv, display_only_table);
+ serv->state = table;
+ }
+ }
+ }
+ }
+}
+
+static void determine_server_state(struct server *serv)
+{
+ switch(serv->state) {
+ case no_players:
+ serv->state = first_player;
+ break;
+ case first_player:
+ if(check_playable_player_number(serv))
+ serv->state = confirmation_waiting;
+ break;
+ case confirmation_waiting:
+ set_record_status_for_all(serv);
+ init_new_game(serv);
+ serv->state = start_game;
+ break;
+ case start_game:
+ set_state_for_client(serv, attack);
+ set_whose_turn(serv, attack);
+ set_record_status_for_all(serv);
+ serv->state = attack_phase_out;
+ break;
+ case attack_phase_out:
+ serv->state = attack_phase_in;
+ break;
+ case attack_phase_in:
+ case defense_phase_in:
+ case tossing_phase_in:
+ if(serv->state == attack_phase_in)
+ define_phase_after_attack(serv);
+ else if(serv->state == defense_phase_in)
+ define_phase_after_defense(serv);
+ else
+ /* all answers received or the limit on card tossing was reached */
+ define_phase_after_tossing(serv);
+ set_record_status_for_all(serv);
+ update_card_quantity_arr(serv);
+ update_game_info(serv->gi, serv->connected_players_counter,
+ serv->shuffled_deck_size, serv->position_whose_turn);
+ break;
+ case table:
+ if(serv->turn_queue[1]->defense_lost) {
+ move_all_cards_to_defender(&serv->cot, &serv->turn_queue[1]->deck);
+ move_turn_queue_two_players_ahead(serv->turn_queue,
+ serv->connected_players_counter);
+ }
+ else {
+ remove_cards_from_table(&serv->cot);
+ shift_turn_queue_by_one(serv->turn_queue,
+ serv->connected_players_counter);
+ }
+ set_record_status_for_all(serv);
+ set_state_for_client(serv, attack);
+ set_whose_turn(serv, attack);
+ update_card_quantity_arr(serv);
+ deal_cards(serv->shuffled_deck, &serv->shuffled_deck_size,
+ serv->turn_queue, serv->connected_players_counter,
+ serv->cc.number_arr);
+ serv->state = attack_phase_out;
+ break;
+ case defense_phase_out:
+ serv->state = defense_phase_in;
+ break;
+ case tossing_phase_out:
+ serv->state = tossing_phase_in;
+ break;
+ }
+ serv->change_server_state = 0;
+}
+
+static int new_player_tracking_condition(enum server_states state)
+{
+ return state == no_players || state == first_player ||
+ state == confirmation_waiting;
+}
+/*
+ * when accepting new players
+ */
+static int server_state_change_condition(struct server *serv)
+{
+ return
+ serv->state == no_players ||
+ (serv->state == first_player && check_playable_player_number(serv)) ||
+ (serv->state == confirmation_waiting &&
+ is_max_playable_player_number(serv));
+
+}
+
+static int accept_new_player(struct server *serv)
+{
+ int accept_result;
+ accept_result = accept_client(serv);
+ if(accept_result == -1) {
+ syslog(LOG_ERR, "accept error");
+ return -1;
+ }
+ /* update info about connected players */
+ set_record_status_for_all(serv);
+ if(server_state_change_condition(serv))
+ serv->change_server_state = 1;
+ return 1;
+}
+
+static void set_up_player_tracking(struct server *serv, fd_set *readfds,
+ fd_set *writefds)
+{
+ int i;
+ for(i = 0; i < serv->max_sess_arr_size; ++i) {
+ if(!serv->sess_arr[i])
+ continue;
+
+ switch(serv->state) {
+ case first_player:
+ if(serv->sess_arr[i]->record)
+ FD_SET(i, writefds);
+ break;
+ case confirmation_waiting:
+ FD_SET(i, readfds);
+ if(serv->sess_arr[i]->record)
+ FD_SET(i, writefds);
+ break;
+ case start_game:
+ case attack_phase_out:
+ case table:
+ case defense_phase_out:
+ case tossing_phase_out:
+ if(serv->sess_arr[i]->record)
+ FD_SET(i, writefds);
+ break;
+ case attack_phase_in:
+ if(serv->sess_arr[i]->state == attack)
+ FD_SET(i, readfds);
+ break;
+ case defense_phase_in:
+ if(serv->sess_arr[i]->state == defense)
+ FD_SET(i, readfds);
+ break;
+ case tossing_phase_in:
+ if(serv->sess_arr[i]->state == tossing &&
+ serv->sess_arr[i]->tm == answer_wait)
+ FD_SET(i, readfds);
+ break;
+ case no_players:
+ {}
+ }
+ }
+}
+
+static int check_server_state_change_conditions(int is_tossing_limit,
+ struct server *serv)
+{
+ switch(serv->state) {
+ case start_game:
+ case attack_phase_out:
+ case attack_phase_in:
+ case defense_phase_out:
+ case defense_phase_in:
+ case tossing_phase_out:
+ case table:
+ return 1;
+ case tossing_phase_in:
+ if(is_tossing_limit ||
+ check_all_answers_were_received((const struct session**)
+ serv->turn_queue,
+ serv->connected_players_counter))
+ return 1;
+ default:
+ return 0;
+ }
+ return 0;
+}
+
+static void close_connection()
+{
+}
+
+static void make_data_transfer(struct server *serv, fd_set *readfds,
+ fd_set *writefds)
+{
+ int i, result, tossing_limit = 0;
+ for(i = 0; i < serv->max_sess_arr_size; ++i) {
+ if(!serv->sess_arr[i])
+ continue;
+
+ switch(serv->state) {
+ case first_player:
+ if(FD_ISSET(i, writefds)) {
+ result = print_message_for_first_player(i);
+ serv->sess_arr[i]->record = 0;
+ }
+ break;
+ case confirmation_waiting:
+ if(FD_ISSET(i, writefds) && serv->sess_arr[i]->record) {
+ result =
+ print_connected_players(i, serv->connected_players_counter);
+ serv->sess_arr[i]->record = 0;
+ }
+ if(FD_ISSET(i, readfds)) {
+ result = check_readiness(serv->sess_arr[i]);
+ if(result == 1)
+ serv->change_server_state = 1;
+ }
+ break;
+ case start_game:
+ case attack_phase_out:
+ case defense_phase_out:
+ case tossing_phase_out:
+ case table:
+ if(FD_ISSET(i, writefds) && serv->sess_arr[i]->record)
+ result = print_game_part(serv->sess_arr[i], serv->gi);
+ serv->sess_arr[i]->record = 0;
+ break;
+ case attack_phase_in:
+ if(FD_ISSET(i, readfds))
+ result = get_cards_from_attacker(serv->sess_arr[i], &serv->cot,
+ serv->turn_queue[1]->deck,
+ &serv->cq);
+ break;
+ case defense_phase_in:
+ if(FD_ISSET(i, readfds))
+ result = get_card_from_defender(serv->sess_arr[i], &serv->cot,
+ serv->trump_suit);
+ break;
+ case tossing_phase_in:
+ if(FD_ISSET(i, readfds)) {
+ /* to determine that the player can no longer tossing */
+ if(tossing_limit)
+ serv->sess_arr[i]->tm = cancel;
+ result =
+ get_cards_from_tossing_player(serv->sess_arr[i],
+ serv->turn_queue[1]->deck,
+ &serv->cot, &serv->cq);
+ if(result == 1 || result == 2)
+ serv->sess_arr[i]->tm = answer_got;
+
+ if(result == 2)
+ tossing_limit = 1;
+ else if(result == 3)
+ serv->sess_arr[i]->tm = cancel;
+ }
+ break;
+ case no_players:
+ {}
+ }
+ }
+ if(serv->state == start_game || serv->state == table)
+ sleep(2);
+ if(check_server_state_change_conditions(tossing_limit, serv))
+ serv->change_server_state = 1;
+ /* connection is closed */
+ if(!result)
+ close_connection();
+}
+
+int main_loop(struct server *serv)
+{
+ int maxfd, accept_result, select_result;
+ fd_set readfds, writefds;
+
+ for(;;) {
+ if(serv->change_server_state)
+ determine_server_state(serv);
+ FD_ZERO(&readfds);
+ FD_ZERO(&writefds);
+
+ if(new_player_tracking_condition(serv->state)) {
+ FD_SET(serv->listen_socket, &readfds);
+ maxfd = serv->listen_socket;
+ }
+
+ set_up_player_tracking(serv, &readfds, &writefds);
+
+ select_result = select(maxfd + 1, &readfds, &writefds, NULL, NULL);
+ if(select_result == -1)
+ return 3;
+
+ if(FD_ISSET(serv->listen_socket, &readfds)) {
+ accept_result = accept_new_player(serv);
+ if(accept_result == -1)
+ return 4;
+ }
+ make_data_transfer(serv, &readfds, &writefds);
+ }
+}
+
+/*
+ * 0 - error
+ * 1 - success
+ */
+static int init_server(struct server *serv, int port)
+{
+ int opt, i;
+ struct sockaddr_in addr;
+
+ serv->listen_socket = socket(AF_INET, SOCK_STREAM, 0);
+ if(serv->listen_socket == -1)
+ return 0;
+
+ opt = 1;
+ setsockopt(serv->listen_socket, SOL_SOCKET, SO_REUSEADDR, &opt,
+ sizeof(opt));
+
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ addr.sin_port = htons(port);
+
+ if(bind(serv->listen_socket, (struct sockaddr*) &addr, sizeof(addr)) == -1)
+ return 0;
+
+ serv->sess_arr = malloc(sizeof(*serv->sess_arr) *
+ init_sess_arr_size);
+ serv->max_sess_arr_size = init_sess_arr_size;
+ for(i = 0; i < init_sess_arr_size; i++)
+ serv->sess_arr[i] = NULL;
+
+ serv->state = no_players;
+ serv->change_server_state = 0;
+ serv->connected_players_counter = 0;
+ serv->shuffled_deck = NULL;
+ serv->shuffled_deck_size = max_shuffled_deck_size;
+ serv->gi = NULL;
+ serv->cc.number_arr = NULL;
+
+ listen(serv->listen_socket, listen_qlen);
+ return 1;
+}
+
+#if 0
+static void demonization()
+{
+ int pid;
+
+ close(0);
+ close(1);
+ close(2);
+ open("/dev/null", O_RDONLY);
+ open("/dev/null", O_WRONLY);
+ open("/dev/null", O_WRONLY);
+ chdir("/");
+ pid = fork();
+ if(pid)
+ exit(0);
+ setsid();
+ pid = fork();
+ if(pid)
+ exit(0);
+}
+#endif
+
+int main(int argc, char **argv)
+{
+ unsigned short port;
+ struct server serv;
+
+ if(argc != 2) {
+ fprintf(stderr,
+ "Usage: <port>\n");
+ return 1;
+ }
+#if 0
+ demonization();
+#endif
+ srand(time(NULL));
+#if 0
+ openlog("durak server", 0, LOG_USER);
+ syslog(LOG_INFO, "daemon started");
+#endif
+ port = strtol(argv[1], NULL, 10);
+
+ if(!init_server(&serv, port)) {
+#if 0
+ syslog(LOG_ERR, "server initialization error");
+#endif
+ return 2;
+ }
+ return main_loop(&serv);
+#if 0
+ syslog(LOG_INFO, "server terminated");
+ closelog();
+#endif
+}
diff --git a/server/server.h b/server/server.h
new file mode 100644
index 0000000..46c965b
--- /dev/null
+++ b/server/server.h
@@ -0,0 +1,99 @@
+#ifndef SERVER_H_SENTRY
+#define SERVER_H_SENTRY
+
+#include "card_queue.h"
+#include "card_stack.h"
+
+enum {
+ max_buffer_size = 4096,
+ listen_qlen = 32,
+ init_sess_arr_size = 32
+};
+
+enum {
+ max_shuffled_deck_size = 52,
+ max_card_arr_size = 18
+};
+
+enum server_states {
+ no_players,
+ first_player,
+ confirmation_waiting,
+ start_game,
+ attack_phase_out,
+ attack_phase_in,
+ defense_phase_out,
+ defense_phase_in,
+ tossing_phase_out,
+ tossing_phase_in,
+ table
+};
+
+enum client_game_states {
+ /* in game */
+ display_only_table = 3,
+ attack_expectation,
+ defense_expectation,
+ tossing_expectation,
+ attack,
+ defense,
+ tossing,
+ card_acceptance_status,
+ tossing_limit_status
+};
+
+enum tossing_mode {
+ cancel,
+ answer_wait,
+ answer_got,
+ none
+};
+
+struct session {
+ int fd;
+ enum client_game_states state;
+ /* read data from client */
+ char buffer[max_buffer_size];
+ int buffer_used;
+ int record;
+ player_cards deck;
+ int player_position;
+ enum tossing_mode tm;
+ int defense_lost;
+};
+
+struct cards_on_table {
+ /*
+ * example: 2^ \ - 10# \ K#
+ */
+ const char* card_arr[max_card_arr_size];
+ int card_arr_idx;
+};
+
+struct card_count {
+ int *number_arr;
+ /* will only store the initial number of players (idx) */
+ int number_arr_idx;
+};
+
+struct server {
+ int change_server_state;
+ enum server_states state;
+ int listen_socket;
+ struct session **sess_arr;
+ int max_sess_arr_size;
+ int connected_players_counter;
+ /* TODO: make static allocation memory */
+ const char **shuffled_deck;
+ int shuffled_deck_size;
+ const char *trump_suit;
+ const char *trump_card;
+ struct game_info *gi;
+ struct session **turn_queue;
+ struct card_count cc;
+ int position_whose_turn;
+ struct card_queue cq;
+ struct cards_on_table cot;
+};
+
+#endif
diff --git a/server/server_data_processing.c b/server/server_data_processing.c
new file mode 100644
index 0000000..30953bf
--- /dev/null
+++ b/server/server_data_processing.c
@@ -0,0 +1,337 @@
+#include "server_data_processing.h"
+#include "server.h"
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+
+enum {
+ output_buffer_size = 1024
+};
+
+static char output_buffer[output_buffer_size];
+
+static int write_to_client(int fd, int length)
+{
+ return write(fd, output_buffer, length);
+}
+
+static int read_from_client(int fd, char *buffer, int max_buffer_size)
+{
+ int read_result;
+ read_result = read(fd, buffer, max_buffer_size);
+ if(read_result <= 0)
+ return 0;
+ /* buffer overflow */
+ if(read_result >= max_buffer_size)
+ return 0;
+ return read_result;
+}
+
+int print_message_for_first_player(int fd)
+{
+ int data_size;
+
+ data_size = sprintf(output_buffer, "%u\n", first_player);
+ return write_to_client(fd, data_size);
+}
+
+int print_connected_players(int fd, int number)
+{
+ int data_size;
+
+ data_size = sprintf(output_buffer, "%u:%u\n", confirmation_waiting, number);
+ return write_to_client(fd, data_size);
+}
+
+/*
+ * 1 - ready
+ * 0 - buffer overflow or player closed session
+ * -1 - not ready
+ */
+int check_readiness(struct session *client)
+{
+ int read_result;
+ read_result = read_from_client(client->fd, client->buffer, max_buffer_size);
+ if(!read_result)
+ return read_result;
+ if(client->buffer[0] == '\n')
+ return 1;
+ else
+ return -1;
+}
+/*
+ * example: 10#\A^\7v'\n'
+ * or 0'\n'
+ */
+static void copy_card_queue(int *offset, struct card_queue *cq)
+{
+ int length, i;
+ struct card_queue_item *next_attack_card;
+
+ next_attack_card = NULL;
+ while((next_attack_card = get_next_card_from_queue(cq, next_attack_card))) {
+ length = strlen(next_attack_card->str);
+ for(i = 0; i < length; ++i, ++*offset)
+ *(output_buffer + *offset) = (next_attack_card->str)[i];
+ /* delimiter */
+ *(output_buffer + *offset) = '\\';
+ ++*offset;
+ }
+ *(output_buffer + *offset-1) = ':';
+}
+
+/*
+ * example: 10#+\+Q#+A^+\+-+7v:
+ * or 0:
+ */
+static void copy_cards_on_table(int *offset, struct cards_on_table cot)
+{
+ int i, j, length;
+ for(i = 0; i <= cot.card_arr_idx; ++i) {
+ length = strlen(cot.card_arr[i]);
+ for(j = 0; j < length; ++j, ++*offset)
+ *(output_buffer + *offset) = cot.card_arr[i][j];
+ /* delimiter */
+ *(output_buffer + *offset) = '+';
+ ++*offset;
+ }
+ *(output_buffer + *offset-1) = ':';
+}
+/*
+ * example: 10#\A^\7v:
+ * or 10#\A^\7v'\n'
+ * or 0:
+ * or 0'\n'
+ */
+static void copy_own_deck(int *offset, player_cards deck)
+{
+ const char *card;
+ int length, i;
+
+ while(deck) {
+ card = deck->str;
+ length = strlen(card);
+ for(i = 0; i < length; ++i, ++*offset)
+ *(output_buffer + *offset) = card[i];
+ *(output_buffer + *offset) = '\\';
+ ++*offset;
+ deck = deck->next;
+ }
+ *(output_buffer + *offset-1) = ':';
+}
+
+static void copy_base_info(int *free_pos, struct game_info *gi,
+ enum client_game_states state, player_cards deck,
+ int player_position)
+{
+ int i;
+
+ *free_pos =
+ sprintf(output_buffer, "%u:%u:%u:%u:%s:", state, gi->players_number,
+ gi->shuffled_cards_left, player_position, gi->trump_card);
+ /*
+ * opponent's card count
+ * example: 2\6\3:
+ */
+ for(i = 0; i < gi->players_number; ++i) {
+
+ *free_pos += sprintf(output_buffer + *free_pos, "%u\\",
+ gi->card_quantity_arr[i]);
+ }
+ *(output_buffer + *free_pos-1) = ':';
+
+ if(gi->cot->card_arr_idx != -1)
+ copy_cards_on_table(free_pos, *gi->cot);
+ else
+ *free_pos += sprintf(output_buffer + *free_pos, "0:");
+
+ if(!is_empty_stack(deck))
+ copy_own_deck(free_pos, deck);
+ else
+ *free_pos += sprintf(output_buffer + *free_pos, "0:");
+}
+
+int print_game_part(const struct session *client, struct game_info *gi)
+{
+ int free_pos; /* free_pos == data length */
+
+ copy_base_info(&free_pos, gi, client->state, client->deck,
+ client->player_position);
+
+ switch(client->state) {
+ case display_only_table:
+ /* let the client print card tips and wait for input */
+ case attack:
+ case defense:
+ /*
+ * who haven't got tossing card (including defender) must wait
+ * when other players make moves
+ */
+ case tossing_expectation:
+ case tossing:
+ /* instead of last ':' */
+ *(output_buffer + free_pos-1) = '\n';
+ break;
+ /* other clents will be waiting particular player */
+ case attack_expectation:
+ free_pos += sprintf(output_buffer + free_pos, "%u:",
+ gi->position_whose_turn);
+ *(output_buffer + free_pos-1) = '\n';
+ break;
+ case defense_expectation:
+ /* copying whose turn */
+ free_pos += sprintf(output_buffer + free_pos, "%u:",
+ gi->position_whose_turn);
+ /* copying card queue */
+ if(is_empty_queue(gi->cq))
+ free_pos += sprintf(output_buffer + free_pos, "0:");
+ else
+ copy_card_queue(&free_pos, gi->cq);
+ *(output_buffer + free_pos-1) = '\n';
+ default:
+ {}
+ }
+ return write_to_client(client->fd, free_pos);
+}
+#if 0
+static int is_receiving_cards_limit(int total_defense_cards, int total_attack_cards)
+{
+ if(total_defense_cards >= start_deck_size &&
+ total_attack_cards == start_deck_size) {
+ return 1;
+ } else if(total_defense_cards == total_attack_cards)
+ return 1;
+ return 0;
+}
+#endif
+
+int get_cards_from_attacker(struct session *client,
+ const struct cards_on_table *cot,
+ const player_cards defense_deck,
+ struct card_queue *cq)
+{
+ int read_result, i, j;
+ int card_return_status = 0, all_cards_received_status = 1;
+ int data_length;
+ char given_card[4];
+ const char *stack_card = NULL;
+
+ read_result = read_from_client(client->fd, client->buffer, max_buffer_size);
+ if(!read_result)
+ return read_result;
+ for(i = 0, j = 0; i < read_result && client->buffer[i] != '\n'; ++i) {
+ if(client->buffer[i] == '\\') {
+ given_card[j] = '\0';
+ j = 0;
+ if(card_return_status) {
+ all_cards_received_status = 0;
+ break;
+ }
+ stack_card = remove_card_from_stack(&client->deck, given_card);
+ push_queue(cq, stack_card);
+ if(is_receiving_cards_limit(cot, defense_deck, cq))
+ card_return_status = 1;
+ continue;
+ }
+ given_card[j] = client->buffer[i];
+ ++j;
+ }
+ 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);
+ return read_result;
+}
+
+/*
+ * 0 - error
+ * 1 - card was bited, not bited, or
+ * defender didn't want to continue defense phase
+ *
+ */
+int get_card_from_defender(struct session *client, struct cards_on_table *cot,
+ const char * trump_suit)
+{
+ int read_result, i;
+ char given_card[4];
+ const char *stack_card = NULL;
+
+ read_result = read_from_client(client->fd, client->buffer, max_buffer_size);
+ if(!read_result)
+ return read_result;
+ for(i = 0; i < read_result && client->buffer[i] != '\n'; ++i)
+ given_card[i] = client->buffer[i];
+ if(given_card[0] == '\n')
+ return 1;
+ given_card[i] = '\0';
+ if(is_card_bit(cot->card_arr[cot->card_arr_idx-2], given_card, trump_suit)){
+ stack_card = remove_card_from_stack(&client->deck, given_card);
+ put_defender_card_on_table(cot, stack_card);
+ return 1;
+ } else
+ return 0;
+}
+
+/*
+ * 0 - buffer overflow or client closed connection
+ * 1 - some number of cards added to card queue
+ * 2 - card toss limit was reached when accepting cards from this player
+ * 3 - player refused to toss the card
+ *
+ * example: 10#\A^\7v\'\n'
+ */
+int get_cards_from_tossing_player(struct session *client,
+ const player_cards defense_deck,
+ struct cards_on_table *cot,
+ struct card_queue *cq)
+{
+ int read_result, i, j;
+ int card_return_status = 0, all_cards_received_status = 1;
+ int data_length;
+ char given_card[4];
+ const char *stack_card = NULL;
+
+ read_result = read_from_client(client->fd, client->buffer, max_buffer_size);
+ if(!read_result)
+ return read_result;
+
+ for(i = 0, j = 0; i < read_result; ++i) {
+ if(client->buffer[i] == '\\') {
+ /* cancellation */
+ if(given_card[0] == '\n')
+ return 3;
+ /* tossing limit was set */
+ if(client->tm == cancel) {
+ /* it's impossible to accept cards due to tossing limit */
+ data_length =
+ sprintf(output_buffer, "%d\n", tossing_limit_status);
+ write_to_client(client->fd, data_length);
+ return 3;
+ }
+ given_card[j] = '\0';
+ j = 0;
+ if(card_return_status) {
+ all_cards_received_status = 0;
+ } else if(is_correct_tossing_card(given_card, cot)) {
+ stack_card = remove_card_from_stack(&client->deck, given_card);
+ push_queue(cq, stack_card);
+ if(is_receiving_cards_limit(cot, defense_deck, cq))
+ {
+ card_return_status = 1;
+ }
+ }
+ continue;
+ }
+ given_card[j] = client->buffer[i];
+ ++j;
+ }
+ /* 7 -- state for result of received cards */
+ 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);
+ if(all_cards_received_status)
+ return 1;
+ else
+ return 2;
+}
diff --git a/server/server_data_processing.h b/server/server_data_processing.h
new file mode 100644
index 0000000..875fd98
--- /dev/null
+++ b/server/server_data_processing.h
@@ -0,0 +1,21 @@
+#ifndef SERVER_DATA_PROCESSING_H_SENTRY
+#define SERVER_DATA_PROCESSING_H_SENTRY
+
+#include "server_game_process.h"
+
+int print_message_for_first_player(int fd);
+int print_connected_players(int fd, int number);
+int check_readiness(struct session *client);
+int print_game_part(const struct session *client, struct game_info *gi);
+int get_cards_from_attacker(struct session *client,
+ const struct cards_on_table *cot,
+ const player_cards defense_deck,
+ struct card_queue *cq);
+int get_card_from_defender(struct session *client, struct cards_on_table *cot,
+ const char *trump_suit);
+int get_cards_from_tossing_player(struct session *client,
+ const player_cards defense_deck,
+ struct cards_on_table *cot,
+ struct card_queue *cq);
+
+#endif
diff --git a/server/server_game_process.c b/server/server_game_process.c
new file mode 100644
index 0000000..8bf4e60
--- /dev/null
+++ b/server/server_game_process.c
@@ -0,0 +1,475 @@
+#include "server_game_process.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+enum { deck_size = 52 };
+
+static const char *basic_deck[deck_size] = {
+ "2^", "3^", "4^", "5^", "6^", "7^", "8^",
+ "9^", "10^", "J^", "Q^", "K^", "A^", "2#",
+ "3#", "4#", "5#", "6#", "7#", "8#", "9#",
+ "10#", "J#", "Q#", "K#", "A#", "2%", "3%",
+ "4%", "5%", "6%", "7%", "8%", "9%", "10%",
+ "j%", "Q%", "K%", "A%", "2v", "3v", "4v",
+ "5v", "6v", "7v", "8v", "9v", "10v", "Jv",
+ "Qv", "Kv", "Av"
+};
+
+static int get_random_idx()
+{
+ return 0 + (int)((double)deck_size*rand()/(RAND_MAX + 1.0));
+}
+
+const char** get_shuffled_deck()
+{
+ int i, new_idx;
+ const char **shuffled_deck = calloc(deck_size, sizeof(char*));
+ for(i = 0; i < deck_size; ++i) {
+ new_idx = get_random_idx();
+ while(shuffled_deck[new_idx])
+ new_idx = get_random_idx();
+ shuffled_deck[new_idx] = basic_deck[i];
+ }
+ return shuffled_deck;
+}
+
+void deal_first_cards(const char **shuffled_deck, int *cards_left,
+ player_cards *deck)
+{
+ int i, last_idx;
+ const char *str;
+ for(i = 0; i < start_deck_size; ++i) {
+ last_idx = *cards_left-1;
+ str = shuffled_deck[last_idx];
+ if(str) {
+ push_stack(deck, str);
+ --*cards_left;
+ shuffled_deck[last_idx] = NULL;
+ }
+ }
+}
+
+void deal_cards(const char **shuffled_deck, int *shuffled_cards_left,
+ struct session **turn_queue, int players_number,
+ int *card_quantity_arr)
+{
+ int i, j, key, player_deck_size, card_shortage_quantity, last_idx;
+ const char *extracted_card = NULL;
+
+ for(i = 0; i < players_number; ++i) {
+ key = turn_queue[i]->player_position - 1;
+ player_deck_size = card_quantity_arr[key];
+ if(player_deck_size < start_deck_size) {
+ card_shortage_quantity = start_deck_size - player_deck_size;
+ for(j = 0; j < card_shortage_quantity; ++j) {
+ last_idx = *shuffled_cards_left-1;
+ /* shuffled deck is empty */
+ if(last_idx == 0 || last_idx < 0)
+ return;
+ extracted_card = shuffled_deck[last_idx];
+ push_stack(&turn_queue[i]->deck, extracted_card);
+ --*shuffled_cards_left;
+ shuffled_deck[last_idx] = NULL;
+ }
+ }
+ }
+}
+
+const char* extract_trump_card(const char **shuffled_deck, int *cards_left)
+{
+ const char *trump_card = NULL;
+
+ trump_card = shuffled_deck[0] ? shuffled_deck[0] : NULL;
+ if(trump_card) {
+ shuffled_deck[0] = NULL;
+ --*cards_left;
+ }
+ return trump_card;
+}
+#if 0
+const char* find_trump_suit(const char **shuffled_deck, int *cards_left)
+{
+ const char *trump_suit = NULL;
+ trump_suit = shuffled_deck[0] ? shuffled_deck[0] : NULL;
+ if(trump_suit) {
+ shuffled_deck[0] = NULL;
+ trump_suit += strlen(trump_suit) - 1;
+ --*cards_left;
+ }
+ return trump_suit;
+}
+#endif
+
+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 find_lowest_trump(player_cards deck, const char *trump_suit)
+{
+ int length, lowest_rank = 0, current_rank;
+ const char *suit = NULL;
+
+ while(deck) {
+ length = strlen(deck->str);
+ suit = deck->str + length - 1;
+ if(*suit == *trump_suit) {
+ current_rank = convert_rank_to_int(deck->str);
+ if(!lowest_rank || lowest_rank > current_rank)
+ lowest_rank = current_rank;
+ }
+ deck = deck->next;
+ }
+ return lowest_rank;
+}
+
+void shift_turn_queue_by_one(struct session **turn_queue, int size)
+{
+ int i;
+ struct session *tmp = turn_queue[0];
+ for(i = 1; i < size; ++i)
+ turn_queue[i-1] = turn_queue[i];
+ turn_queue[i-1] = tmp;
+}
+
+void move_turn_queue_two_players_ahead(struct session **turn_queue, int size)
+{
+ int i;
+
+ for(i = 0; i < 2; ++i)
+ shift_turn_queue_by_one(turn_queue, size);
+}
+
+void update_card_quantity_arr(struct server *serv)
+{
+ int i, player_position_idx;
+
+ for(i = 0; i < serv->max_sess_arr_size; ++i)
+ if(serv->sess_arr[i]) {
+ player_position_idx = serv->sess_arr[i]->player_position - 1;
+ serv->cc.number_arr[player_position_idx] =
+ find_out_card_quantity_in_deck(serv->sess_arr[i]->deck);
+ }
+}
+
+struct game_info* get_new_game_info(int players_number, int *card_quantity_arr,
+ int shuffled_cards_left,
+ const char *trump_card,
+ int position_whose_turn,
+ struct card_queue *cq,
+ struct cards_on_table *cot)
+
+{
+ struct game_info *gi = malloc(sizeof(struct game_info));
+ gi->players_number = players_number;
+ gi->card_quantity_arr = card_quantity_arr;
+ gi->shuffled_cards_left = shuffled_cards_left;
+ gi->trump_card = trump_card;
+ gi->position_whose_turn = position_whose_turn;
+ gi->cq = cq;
+ gi->cot = cot;
+ return gi;
+}
+
+void update_game_info(struct game_info *gi, int players_number,
+ int shuffled_cards_left, int position_whose_turn)
+{
+ gi->players_number = players_number;
+ gi->shuffled_cards_left = shuffled_cards_left;
+ gi->position_whose_turn = position_whose_turn;
+}
+
+int is_card_bit(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;
+}
+/*
+ * analyze that each attacker card can be beat
+ * TODO: free memory
+ */
+int check_defender_can_defend(struct card_queue *cq,
+ player_cards deck, const char *trump_suit)
+{
+ int is_bited_card;
+ struct card_stack_item *next_defend_card;
+ struct card_queue_item *next_attack_card;
+ player_cards involved_cards;
+
+ is_bited_card = 0;
+ init_stack(&involved_cards);
+
+ next_attack_card = NULL;
+ while((next_attack_card = get_next_card_from_queue(cq, next_attack_card))) {
+ /* get first defend card */
+ next_defend_card = NULL;
+ next_defend_card = get_next_card_from_stack(deck, next_defend_card);
+
+ /* stack traversal */
+ while(next_defend_card) {
+ if(is_card_bit(next_attack_card->str, next_defend_card->str,
+ trump_suit))
+ /* if card doesn't meet */
+ if(!find_card_in_stack(involved_cards, next_defend_card->str)) {
+ push_stack(&involved_cards, next_defend_card->str);
+ is_bited_card = 1;
+ break;
+ }
+ get_next_card_from_stack(deck, next_defend_card);
+ }
+ if(is_bited_card)
+ is_bited_card = 0;
+ else
+ return 0;
+ }
+ return 1;
+}
+
+static 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;
+}
+
+int check_someone_can_toss_card(struct session **turn_queue,
+ int players_quantity,
+ struct card_queue *cq,
+ struct cards_on_table *cot)
+{
+ int i, j;
+ struct card_queue_item *attack_card_in_queue;
+ struct card_stack_item *not_defender_card;
+
+ for(i = 0; i < players_quantity; ++i) {
+ /* skipping defender's cards */
+ if(i == 1)
+ continue;
+
+ /* checking card queue of attack cards */
+ attack_card_in_queue = NULL;
+ while((attack_card_in_queue =
+ get_next_card_from_queue(cq, attack_card_in_queue))) {
+ not_defender_card = NULL;
+ while((not_defender_card =
+ get_next_card_from_stack(turn_queue[i]->deck,
+ not_defender_card)) &&
+ !check_matched_ranks(attack_card_in_queue->str,
+ not_defender_card->str))
+ {}
+ if(not_defender_card)
+ return 1;
+ }
+ /* checking cards on table */
+ for(j = cot->card_arr_idx; j >= 0; --j) {
+ if(cot->card_arr[j][0] == '-' || cot->card_arr[j][0] == '\\')
+ continue;
+ not_defender_card = NULL;
+ while((not_defender_card =
+ get_next_card_from_stack(turn_queue[i]->deck,
+ not_defender_card)) &&
+ !check_matched_ranks(cot->card_arr[j],
+ not_defender_card->str))
+ {}
+ if(not_defender_card)
+ return 1;
+ }
+ }
+ return 0;
+}
+/* is used to make the client's state tossing */
+int check_player_can_toss_card(player_cards deck, struct cards_on_table *cot)
+{
+ int i;
+ struct card_stack_item *not_defender_card;
+
+ /* checking cards on table */
+ for(i = cot->card_arr_idx; i >= 0; --i) {
+ if(cot->card_arr[i][0] == '-' || cot->card_arr[i][0] == '\\')
+ continue;
+ not_defender_card = NULL;
+ while((not_defender_card =
+ get_next_card_from_stack(deck, not_defender_card)) &&
+ !check_matched_ranks(cot->card_arr[i], not_defender_card->str))
+ {}
+ if(not_defender_card)
+ return 1;
+ }
+ return 0;
+}
+
+int is_correct_tossing_card(const char *card, struct cards_on_table *cot)
+{
+ int i;
+
+ for(i = cot->card_arr_idx; i >= 0; --i) {
+ if(cot->card_arr[i][0] == '-' || cot->card_arr[i][0] == '\\')
+ continue;
+ if(check_matched_ranks(cot->card_arr[i], card))
+ return 1;
+ }
+ return 0;
+}
+
+/* when defender cannot bit any card */
+void put_all_cards_from_queue_to_table(struct cards_on_table *cot,
+ struct card_queue *cq)
+{
+ const char *card;
+
+ while(!is_empty_queue(cq)) {
+ card = pop_card_queue(cq);
+ ++cot->card_arr_idx;
+ cot->card_arr[cot->card_arr_idx] = card;
+ ++cot->card_arr_idx;
+ cot->card_arr[cot->card_arr_idx] = "\\";
+ ++cot->card_arr_idx;
+ cot->card_arr[cot->card_arr_idx] = "-";
+ }
+}
+
+void put_one_card_from_queue_to_table(struct cards_on_table *cot,
+ struct card_queue *cq)
+{
+ const char *card;
+
+ card = pop_card_queue(cq);
+ ++cot->card_arr_idx;
+ cot->card_arr[cot->card_arr_idx] = card;
+ ++cot->card_arr_idx;
+ cot->card_arr[cot->card_arr_idx] = "\\";
+ ++cot->card_arr_idx;
+ cot->card_arr[cot->card_arr_idx] = "-";
+}
+
+/* it used by defender to bit attack card */
+void put_defender_card_on_table(struct cards_on_table *cot, const char *card)
+{
+ cot->card_arr[cot->card_arr_idx] = card;
+}
+
+void move_all_cards_to_defender(struct cards_on_table *cot, player_cards *deck)
+{
+ int i;
+#if 0
+ const char *card;
+
+ while(!is_empty_queue(cq)) {
+ card = pop_card_queue(cq);
+ push_stack(deck, card);
+ }
+#endif
+ for(i = cot->card_arr_idx; i >= 0; --i) {
+ if(cot->card_arr[i][0] == '-' || cot->card_arr[i][0] == '\\')
+ continue;
+ push_stack(deck, cot->card_arr[i]);
+ }
+ cot->card_arr_idx = i;
+}
+
+void remove_cards_from_table(struct cards_on_table *cot)
+{
+ cot->card_arr_idx = -1;
+}
+
+int calculate_defense_card_quantity_on_table(const struct cards_on_table *cot)
+{
+ int i, counter = 0;
+
+ for(i = 0; i <= cot->card_arr_idx; ++i) {
+ /* every 3 position meets a defender card */
+ if(!((i+1) % 3)) {
+ if(!strcmp(cot->card_arr[i], "-"))
+ continue;
+ else
+ ++counter;
+ }
+ }
+ return counter;
+}
+
+int calculate_attack_card_quantity_on_table(const struct cards_on_table *cot)
+{
+ int i, counter = 0;
+
+ for(i = 0; i <= cot->card_arr_idx; ++i)
+ if(i == 0 || !(i % 3))
+ ++counter;
+ return counter;
+}
+
+int check_all_answers_were_received(const struct session **turn_queue, int size)
+{
+ int i;
+
+ for(i = 0; i < size; ++i)
+ if(turn_queue[i]->tm == answer_wait)
+ return 0;
+ return 1;
+}
+
+int is_receiving_cards_limit(const struct cards_on_table *cot,
+ player_cards defense_deck,
+ const struct card_queue *cq)
+{
+ int total_defense_cards, total_attack_cards;
+
+ total_defense_cards = calculate_defense_card_quantity_on_table(cot);
+ total_attack_cards = calculate_attack_card_quantity_on_table(cot);
+ total_defense_cards +=
+ find_out_card_quantity_in_deck(defense_deck);
+ total_attack_cards +=
+ find_out_card_quantity_in_cq(cq);
+
+ if(total_defense_cards >= start_deck_size &&
+ total_attack_cards == start_deck_size) {
+ return 1;
+ } else if(total_defense_cards == total_attack_cards)
+ return 1;
+ return 0;
+}
diff --git a/server/server_game_process.h b/server/server_game_process.h
new file mode 100644
index 0000000..8891a56
--- /dev/null
+++ b/server/server_game_process.h
@@ -0,0 +1,78 @@
+#ifndef SERVER_GAME_PROCESS_H_SENTRY
+#define SERVER_GAME_PROCESS_H_SENTRY
+
+#include "card_stack.h"
+#include "server.h"
+#include "card_queue.h"
+
+enum {
+ start_deck_size = 6
+};
+
+struct game_info {
+ int players_number;
+ int *card_quantity_arr;
+ int shuffled_cards_left;
+ const char* trump_card;
+ int position_whose_turn;
+ struct cards_on_table *cot;
+ struct card_queue *cq;
+};
+
+const char** get_shuffled_deck();
+void deal_first_cards(const char **shuffled_deck, int *cards_left,
+ player_cards *deck);
+void deal_cards(const char **shuffled_deck, int *shuffled_cards_left,
+ struct session **turn_queue, int players_number,
+ int *card_quantity_arr);
+const char* extract_trump_card(const char **shuffled_deck, int *cards_left);
+#if 0
+const char* find_trump_suit(const char **shuffled_deck, int *cards_left);
+#endif
+int find_lowest_trump(player_cards deck, const char *trump);
+void shift_turn_queue_by_one(struct session **turn_queue, int size);
+void move_turn_queue_two_players_ahead(struct session **turn_queue, int size);
+void update_card_quantity_arr(struct server *serv);
+
+struct game_info* get_new_game_info(int players_number, int *card_quantity_arr,
+ int shuffled_cards_left,
+ const char *trump_card,
+ int position_whose_turn,
+ struct card_queue *cq,
+ struct cards_on_table *cot);
+
+int is_card_bit(const char *attack_card, const char *defend_card,
+ const char *trump_suit);
+
+int check_defender_can_defend(struct card_queue *cq, player_cards deck,
+ const char *trump_suit);
+
+void update_game_info(struct game_info *gi, int players_number,
+ int shuffled_cards_left, int position_whose_turn);
+
+int check_someone_can_toss_card(struct session **turn_queue,
+ int players_quantity, struct card_queue *cq,
+ struct cards_on_table *cot);
+
+int check_player_can_toss_card(player_cards deck, struct cards_on_table *cot);
+int is_correct_tossing_card(const char *card, struct cards_on_table *cot);
+
+void put_all_cards_from_queue_to_table(struct cards_on_table *cot,
+ struct card_queue *cq);
+
+void put_one_card_from_queue_to_table(struct cards_on_table *cot,
+ struct card_queue *cq);
+
+void put_defender_card_on_table(struct cards_on_table *cot, const char *card);
+
+void move_all_cards_to_defender(struct cards_on_table *cot, player_cards *deck);
+void remove_cards_from_table(struct cards_on_table *cot);
+
+int calculate_defense_card_quantity_on_table(const struct cards_on_table *cot);
+int calculate_attack_card_quantity_on_table(const struct cards_on_table *cot);
+int check_all_answers_were_received(const struct session **turn_queue,
+ int size);
+int is_receiving_cards_limit(const struct cards_on_table *cot,
+ player_cards defense_deck,
+ const struct card_queue *cq);
+#endif