// /*--------------------------------------------------------*\ // | Main Program: BugsWorld Server // |*--------------------------------------------------------*| // | Date: August 25, 1999 // | Author: Paolo Bucci // | // | Brief User's Manual: // | // | // | // | // \*--------------------------------------------------------*/ ///------------------------------------------------------------- /// Global Context --------------------------------------------- ///------------------------------------------------------------- // To get "exit", use RAW_Foundation.h; otherwise RESOLVE_Foundation.h #include "RAW_Foundation.h" #include "CT/Partial_Map/Kernel_2.h" #include "CT/Queue/Kernel_1a.h" #include "CI/Unique_Id_Generator/1.h" #include "CI/Random/Uniform_Generator_1.h" #include "CI/Integer/Hash_1.h" #include "CI/Geometry/Utility_1.h" #include "CI/World/Kernel_1a.h" #include "CT/Randomizing_Machine/Kernel_1.h" ///------------------------------------------------------------- /// Local Context ---------------------------------------------- ///------------------------------------------------------------- enumeration Client_State { start = 0, reverse_connection_get_host, reverse_connection_get_port, reverse_connection_get_display, forward_connection, identify, birth, size, connected, send, dead, suspended }; ///------------------------------------------------------------- enumeration Instruction { MOVE, TURNLEFT, TURNRIGHT, INFECT, SKIP }; ///------------------------------------------------------------- enumeration Bug { ACTIVE, BIRTH, DEATH }; ///------------------------------------------------------------- enumeration Cell { EMPTY, WALL, FRIEND, ENEMY }; ///------------------------------------------------------------- concrete_instance class Stream_Info : instantiates Record < Character_IStream, Character_OStream, Text, Text > {}; ///------------------------------------------------------------- field_name (Stream_Info, 0, Character_IStream, from); field_name (Stream_Info, 1, Character_OStream, to); field_name (Stream_Info, 2, Text, inbuffer); field_name (Stream_Info, 3, Text, outbuffer); ///------------------------------------------------------------- concrete_instance class Connection_Info : instantiates Record < Text, Integer, Boolean, Integer > {}; ///------------------------------------------------------------- field_name (Connection_Info, 0, Text, host); field_name (Connection_Info, 1, Integer, port); field_name (Connection_Info, 2, Boolean, display); field_name (Connection_Info, 3, Integer, connect_attempts); ///------------------------------------------------------------- concrete_instance class Client_Info : instantiates Record < Integer, Integer, Stream_Info, Connection_Info > {}; ///------------------------------------------------------------- field_name (Client_Info, 0, Integer, state); field_name (Client_Info, 1, Integer, bug_count); field_name (Client_Info, 2, Stream_Info, io); field_name (Client_Info, 3, Connection_Info, connection); ///------------------------------------------------------------- concrete_instance class Client_Map : instantiates Partial_Map_Kernel_2 < Integer, Client_Info, Integer_Hash_1, 20 > {}; ///------------------------------------------------------------- concrete_instance class Request_Info : instantiates Record < Integer, Integer, Integer > {}; ///------------------------------------------------------------- field_name (Request_Info, 0, Integer, species_id); field_name (Request_Info, 1, Integer, bug_id); field_name (Request_Info, 2, Integer, action); ///------------------------------------------------------------- concrete_instance class Request_Queue : instantiates Queue_Kernel_1a {}; ///------------------------------------------------------------- concrete_instance class Request_Scheduler : instantiates Randomizing_Machine_Kernel_1 {}; ///------------------------------------------------------------- concrete_instance class Message_Info : instantiates Record < Integer, Integer, Integer, Integer, Integer, Integer > {}; ///------------------------------------------------------------- field_name (Message_Info, 0, Integer, msg_species_id); field_name (Message_Info, 1, Integer, msg_bug_id); field_name (Message_Info, 2, Integer, msg_col); field_name (Message_Info, 3, Integer, msg_row); field_name (Message_Info, 4, Integer, msg_dir); field_name (Message_Info, 5, Integer, status); ///------------------------------------------------------------- concrete_instance class Message_Queue : instantiates Queue_Kernel_1a {}; ///------------------------------------------------------------- concrete_instance class World : instantiates World_Kernel_1a {}; ///------------------------------------------------------------- concrete_instance class Unique_Id_Generator : instantiates Unique_Id_Generator_1 {}; ///------------------------------------------------------------- concrete_instance class Random_Number_Generator : instantiates Random_Uniform_Generator_1 {}; ///------------------------------------------------------------- concrete_instance class Geometry : instantiates Geometry_Utility_1 {}; ///------------------------------------------------------------- /// Global objects --------------------------------------------- ///------------------------------------------------------------- #define DEFAULT_READ_BLOCK_LENGTH 50 #define MAX_CONNECTION_ATTEMPTS 50 #define MAX_WORLD_SIZE 30 ///------------------------------------------------------------- object Character_IStream ins; object Character_OStream outs; object Unique_Id_Generator client_id_generator, bug_id_generator; object Text server_host_name; object Integer server_port_number; object Integer columns, rows, max_age, initial_number_of_bugs; object Integer number_of_bugs, max_number_of_bugs; object Integer number_of_clients, max_number_of_clients; object Integer number_of_displays, max_number_of_displays; object Integer tournament_delay, tournament_duration; object Random_Number_Generator col_gen, row_gen, dir_gen; ///------------------------------------------------------------- /// Global operations ------------------------------------------ ///------------------------------------------------------------- global_procedure Advertise_Server_Host_And_Port (); ///------------------------------------------------------------- global_procedure Append_Message ( alters Text& buffer, preserves Integer id, preserves Integer view, preserves Integer stat ); ///------------------------------------------------------------- global_procedure Append_Messages_To_Client_Buffers ( preserves World& w, alters Client_Map& cm, alters Message_Queue& messages ); ///------------------------------------------------------------- global_procedure Append_String ( alters Text& target, consumes Text& source ); ///------------------------------------------------------------- global_procedure Broadcast_World_To_Displays ( alters Client_Map& dm, preserves Text message, preserves Integer time ); ///------------------------------------------------------------- global_procedure Check_For_New_Connection_Request ( produces Integer& client_id, produces Client_Info& client_info ); ///------------------------------------------------------------- global_procedure Create_Bugs ( alters World& w, preserves Integer client_id, preserves Integer time, alters Client_Info& ci ); ///------------------------------------------------------------- global_procedure Do_One_Iteration ( alters World& w, alters Client_Map& cm, alters Client_Map& dm, preserves Integer c ); ///------------------------------------------------------------- global_procedure Extract_String ( alters Text& source, produces Text& result, produces Boolean& eol, produces Boolean& eos ); ///------------------------------------------------------------- global_procedure Get_Display ( preserves Integer client_id, alters Client_Info& ci ); ///------------------------------------------------------------- global_procedure Get_Host ( preserves Integer client_id, alters Client_Info& ci ); ///------------------------------------------------------------- global_procedure Get_Port ( preserves Integer client_id, alters Client_Info& ci ); ///------------------------------------------------------------- global_procedure Get_Requests ( preserves World& w, preserves Integer client_id, preserves Integer time, alters Client_Info& ci, alters Request_Scheduler& rs ); ///------------------------------------------------------------- global_procedure Identify_Client ( preserves Integer client_id, alters Client_Info& ci, preserves Integer time ); ///------------------------------------------------------------- global_function Integer Local_View ( preserves World& w, preserves Integer spec, preserves Integer col, preserves Integer row, preserves Integer dir ); ///------------------------------------------------------------- global_procedure Make_Forward_Connection ( preserves Integer client_id, alters Client_Info& ci ); ///------------------------------------------------------------- global_procedure Process_Arguments ( preserves Command_Line_Handler& args ); ///------------------------------------------------------------- global_procedure Process_Client_Requests ( alters World& w, alters Client_Map& cm, consumes Request_Scheduler& rs, produces Message_Queue& messages ); ///------------------------------------------------------------- global_procedure Read_Line_From_Client ( alters Character_IStream& is, alters Text& buf, preserves Integer max, produces Boolean& eol, produces Boolean& eos ); ///------------------------------------------------------------- global_procedure Scan_World ( alters World& w, alters Client_Map& cm, alters Message_Queue& client_msgs, produces Text& display_msg ); ///------------------------------------------------------------- global_procedure Send ( preserves Integer client_id, alters Client_Info& ci, preserves Integer time ); ///------------------------------------------------------------- global_procedure Send_World_Size ( preserves Integer client_id, alters Client_Info& ci, preserves Integer time ); ///------------------------------------------------------------- /// Global operation bodies ------------------------------------ ///------------------------------------------------------------- procedure_body Advertise_Server_Host_And_Port () { ins.Get_Host_And_Port (server_host_name, server_port_number); outs << "Listening at " << server_host_name << ":" << server_port_number << "\n"; } ///------------------------------------------------------------- procedure_body Append_Message ( alters Text& buffer, preserves Integer id, preserves Integer view, preserves Integer stat ) { object Character_OStream output; object Text str; object Character ch; output.Open_Internal (); output << id << "\n" << view << "\n" << stat << "\n"; output.Close_Internal (str); while (str.Length () > 0) { str.Remove (0, ch); buffer.Add (buffer.Length (), ch); } } ///------------------------------------------------------------- procedure_body Append_Messages_To_Client_Buffers ( preserves World& w, alters Client_Map& cm, alters Message_Queue& messages ) { object Message_Info msg; // enqueue messages on clients' buffers while (messages.Length () > 0) { messages.Dequeue (msg); if (cm.Is_Defined (msg[msg_species_id])) { if (msg[status] == DEATH) { Append_Message (cm[msg[msg_species_id]][io][outbuffer], msg[msg_bug_id], EMPTY, DEATH); } else { Append_Message ( cm[msg[msg_species_id]][io][outbuffer], msg[msg_bug_id], Local_View (w, msg[msg_species_id], msg[msg_col], msg[msg_row], msg[msg_dir]), msg[status]); } cm[msg[msg_species_id]][state] = send; } } } ///------------------------------------------------------------- procedure_body Append_String ( alters Text& target, consumes Text& source ) { object Character ch; while (source.Length () > 0) { source.Remove (0, ch); target.Add (target.Length (), ch); } } ///------------------------------------------------------------- procedure_body Broadcast_World_To_Displays ( alters Client_Map& dm, preserves Text message, preserves Integer time ) { object Client_Map tmp; object Text response; object Integer display_id; object Client_Info display_info; while (dm.Size () > 0) { dm.Undefine_Any (display_id, display_info); if (display_info[io][from].OK_To_Read ()) { display_info[io][from] >> response; display_info[io][to] << time << "\n" << message << "\n"; tmp.Define (display_id, display_info); } else if (display_info[io][from].At_EOS ()) { display_info[io][from].Close_External (); display_info[io][to].Close_External (); outs << " Connection closed by client - display (" << display_id << "/" << time << ")\n"; number_of_displays--; } else { tmp.Define (display_id, display_info); } } dm &= tmp; } ///------------------------------------------------------------- procedure_body Check_For_New_Connection_Request ( produces Integer& client_id, produces Client_Info& client_info ) { object Character_IStream is; client_info.Clear (); is.Open_External (); if (is.Is_Open ()) { // Assign new client an available id number client_id_generator.Generate (client_id); client_info[state] = reverse_connection_get_host; client_info[io][from] &= is; } else { client_id = 0; } } ///------------------------------------------------------------- procedure_body Create_Bugs ( alters World& w, preserves Integer client_id, preserves Integer time, alters Client_Info& ci ) { object Integer n = initial_number_of_bugs; object Message_Queue messages; object Message_Info msg; // Create the new client's creatures while (n > 0) { object Boolean done; while (not done) { // generate random position and check it is empty col_gen.Generate_Next (); msg[msg_col] = col_gen.Uniform_Integer (1, columns); row_gen.Generate_Next (); msg[msg_row] = row_gen.Uniform_Integer (1, rows); if (w.Cell_Content (msg[msg_col], msg[msg_row]) == world_empty_cell) { done = true; } } // generate random direction dir_gen.Generate_Next (); msg[msg_dir] = dir_gen.Uniform_Integer (NORTH, WEST); // 0, 1, 2, 3 bug_id_generator.Generate (msg[msg_bug_id]); w.Add_Bug (msg[msg_bug_id], client_id, msg[msg_col], msg[msg_row], msg[msg_dir], 0); msg[msg_species_id] = client_id; msg[status] = BIRTH; messages.Enqueue (msg); n--; } ci[bug_count] = initial_number_of_bugs; while (messages.Length () > 0) { messages.Dequeue (msg); Append_Message ( ci[io][outbuffer], msg[msg_bug_id], Local_View (w, msg[msg_species_id], msg[msg_col], msg[msg_row], msg[msg_dir]), msg[status]); } // send birth certificates to client (unless in tournament delay) if (tournament_delay == 0) // no tournament delay { ci[state] = send; Send (client_id, ci, time); } else { ci[state] = suspended; } } ///------------------------------------------------------------- procedure_body Do_One_Iteration ( alters World& w, alters Client_Map& cm, alters Client_Map& dm, preserves Integer c ) { object Client_Map tmp; object Client_Info ci; object Integer id; object Request_Scheduler rs; object Message_Queue client_messages; object Text display_message; Check_For_New_Connection_Request (id, ci); if (ci[state] == reverse_connection_get_host) // new client { outs << "Connection requested (" << id << "/" << c << ")...\n"; cm.Define (id, ci); } while (cm.Size () > 0) // for each client in cm { cm.Undefine_Any (id, ci); case_select (ci[state]) { case reverse_connection_get_host: { Get_Host (id, ci); } break; case reverse_connection_get_port: { Get_Port (id, ci); } break; case reverse_connection_get_display: { Get_Display (id, ci); } break; case forward_connection: { Make_Forward_Connection (id, ci); } break; case identify: { Identify_Client (id, ci, c); } break; case birth: { outs << " Connection established - client (" << id << "/" << c << ")\n"; Create_Bugs (w, id, c, ci); } break; case size: { outs << " Connection established - display (" << id << "/" << c << ")\n"; Send_World_Size (id, ci, c); } break; case connected: { if (ci[bug_count] == 0) { ci[state] = dead; outs << " Connection completed - client (" << id << "/" << c << ") (all bugs infected)\n"; number_of_clients--; } else { Get_Requests (w, id, c, ci, rs); } } break; case send: { Send (id, ci, c); } break; case suspended: { if (c == tournament_delay) { ci[state] = send; } } break; default: break; } if (ci[state] != dead) { if (ci[connection][display] and (ci[state] == connected)) { dm.Define (id, ci); } else { tmp.Define (id, ci); } } else // get rid of this client { if (ci[io][from].Is_Open ()) { ci[io][from].Close_External (); if (ci[io][to].Is_Open ()) { ci[io][to].Close_External (); } } } } cm &= tmp; number_of_bugs = w.Number_Of_Bugs (); Process_Client_Requests (w, cm, rs, client_messages); Scan_World (w, cm, client_messages, display_message); Append_Messages_To_Client_Buffers (w, cm, client_messages); Broadcast_World_To_Displays (dm, display_message, c); } ///------------------------------------------------------------- procedure_body Extract_String ( alters Text& source, produces Text& result, produces Boolean& eol, produces Boolean& eos ) { object Character ch; result.Clear (); eol = false; while ((source.Length () > 0) and not eol) { source.Remove (0, ch); if (ch == '\n') { eol = true; } else { result.Add (result.Length (), ch); } } eos = (source.Length () == 0); } ///------------------------------------------------------------- procedure_body Get_Display ( preserves Integer client_id, alters Client_Info& ci ) { object Boolean at_eol, at_eos; object Text msg; Read_Line_From_Client (ci[io][from], ci[io][inbuffer], DEFAULT_READ_BLOCK_LENGTH, at_eol, at_eos); if (at_eos) { ci[state] = dead; outs << " Connection aborted by client (" << client_id << ")\n"; } else { if (at_eol) // a newline was found { object Text msg; if (Can_Convert_To_Boolean (ci[io][inbuffer])) { ci[connection][display] = To_Boolean (ci[io][inbuffer]); ci[io][inbuffer].Clear (); ci[state] = forward_connection; } else { ci[state] = dead; outs << " Connection aborted (" << client_id << ") (invalid display flag)\n"; } } } } ///------------------------------------------------------------- procedure_body Get_Host ( preserves Integer client_id, alters Client_Info& ci ) { object Boolean at_eol, at_eos; object Text msg; Read_Line_From_Client (ci[io][from], ci[io][inbuffer], DEFAULT_READ_BLOCK_LENGTH, at_eol, at_eos); if (at_eos) { ci[state] = dead; outs << " Connection aborted by client (" << client_id << ")\n"; } else { if (at_eol) // a newline was found { object Boolean ok; Character_OStream::Check_Host_Name (ci[io][inbuffer], ok, msg); if (ok) { ci[connection][host] &= ci[io][inbuffer]; ci[state] = reverse_connection_get_port; } else { ci[state] = dead; outs << " Connection aborted (" << client_id << ") (" << msg << ")\n"; } } } } ///------------------------------------------------------------- procedure_body Get_Port ( preserves Integer client_id, alters Client_Info& ci ) { object Boolean at_eol, at_eos; object Text msg; Read_Line_From_Client (ci[io][from], ci[io][inbuffer], DEFAULT_READ_BLOCK_LENGTH, at_eol, at_eos); if (at_eos) { ci[state] = dead; outs << " Connection aborted by client (" << client_id << ")\n"; } else { if (at_eol) // a newline was found { object Text msg; if (Can_Convert_To_Integer (ci[io][inbuffer])) { ci[connection][port] = To_Integer (ci[io][inbuffer]); if ((ci[connection][port] == server_port_number) and (ci[connection][host] == server_host_name)) { ci[state] = dead; outs << " Connection aborted (" << client_id << ") (invalid port: server port)\n"; } else if ((ci[connection][port] < 1024) or (ci[connection][port] > 65535)) { ci[state] = dead; outs << " Connection aborted (" << client_id << ") (invalid port: out of range number)\n"; } else { ci[io][inbuffer].Clear (); ci[state] = reverse_connection_get_display; } } else { ci[state] = dead; outs << " Connection aborted (" << client_id << ") (invalid port: not a number)\n"; } } } } ///------------------------------------------------------------- procedure_body Get_Requests ( preserves World& w, preserves Integer client_id, preserves Integer time, alters Client_Info& ci, alters Request_Scheduler& rs ) { object Integer count, complete_requests; object Boolean done, at_eol, at_eos; object Text msg, buffer; // try to read one request for 50% of the client's bugs // I am using 50% (instead of the original 100%) because otherwise // it seems that once one client has a lot of bugs and the // competing client has relatively few, the first client (or the // communication channel to it) cannot keep up--this gives an // advantage to the weaker species and creates kind of cyclical // behavior where neither species manages to wipe out the other ci[io][inbuffer] &= buffer; while ((count < /* 2 * */ ci[bug_count]) and not done) { Read_Line_From_Client (ci[io][from], buffer, 10, at_eol, at_eos); if (at_eos) { ci[state] = dead; number_of_clients--; outs << " Connection closed by client - client (" << client_id << "/" << time << ")\n"; done = true; } else if (not at_eol) { done = true; } else { buffer.Add (buffer.Length (), '\n'); count++; } } if (ci[state] != dead) { object Request_Queue rq; object Request_Info ri; object Text bug_text, action_text; object Integer bug_value, action_value; // check each request and add it to queue done = false; while (not done) { Extract_String (buffer, bug_text, at_eol, at_eos); if (at_eos) { Append_String (ci[io][inbuffer], bug_text); if (at_eol) { ci[io][inbuffer].Add (ci[io][inbuffer].Length (), '\n'); } done = true; } else { Extract_String (buffer, action_text, at_eol, at_eos); if (not at_eol) { Append_String (ci[io][inbuffer], bug_text); ci[io][inbuffer].Add (ci[io][inbuffer].Length (), '\n'); Append_String (ci[io][inbuffer], action_text); done = true; } else { if (Can_Convert_To_Integer (bug_text) and Can_Convert_To_Integer (action_text)) { bug_value = To_Integer (bug_text); action_value = To_Integer (action_text); // if (not w.Is_Member (bug_value)) // { // ci[state] = dead; // outs << " Connection Closed - Client ID: " // << client_id << " (invalid bug_id)\n"; // done = true; // } // else if ((action_value < MOVE) or (action_value > SKIP)) { ci[state] = dead; number_of_clients--; outs << " Connection closed - client (" << client_id << "/" << time << ") (invalid action)\n"; done = true; } else if (w.Is_Member (bug_value)) { ri[species_id] = client_id; ri[bug_id] = bug_value; ri[action] = action_value; rq.Enqueue (ri); } } else { ci[state] = dead; number_of_clients--; outs << " Connection closed - client (" << client_id << "/" << time << ") (invalid input)\n"; done = true; } } } } if (ci[state] != dead) { // move client's requests from queue to scheduler, // ONLY if client is still alive--otherwise, ignore them while (rq.Length () > 0) { rq.Dequeue (ri); rs.Insert (ri); } } } } ///------------------------------------------------------------- procedure_body Identify_Client ( preserves Integer client_id, alters Client_Info& ci, preserves Integer time ) { object Text msg; if (ci[io][to].OK_To_Write ()) { if (ci[connection][display] and (number_of_displays < max_number_of_displays)) { ci[io][to] << client_id << "\n"; ci[state] = size; ci[connection][connect_attempts] = 0; number_of_displays++; } else if (not ci[connection][display] and (number_of_clients < max_number_of_clients) and (number_of_bugs + initial_number_of_bugs < max_number_of_bugs)) { if ((tournament_delay == 0) or (time <= tournament_delay)) { ci[io][to] << client_id << "\n"; ci[state] = birth; ci[connection][connect_attempts] = 0; number_of_clients++; number_of_bugs += initial_number_of_bugs; } else { ci[io][to] << "-2\n"; ci[state] = dead; outs << " Connection refused - client (" << client_id << ") (tournament in progress)\n"; } } else { ci[io][to] << "-1\n"; ci[state] = dead; if (ci[connection][display]) { outs << " Connection refused - display (" << client_id << ") (exceeded max number of connections allowed)\n"; } else { outs << " Connection refused - client (" << client_id << ") (exceeded max number of connections allowed)\n"; } } } else if (ci[io][from].At_EOS ()) { ci[state] = dead; if (ci[connection][display]) { outs << " Connection aborted by client - display (" << client_id << ")\n"; } else { outs << " Connection aborted by client - client (" << client_id << ")\n"; } } else { ci[connection][connect_attempts]++; if (ci[connection][connect_attempts] == MAX_CONNECTION_ATTEMPTS) { ci[state] = dead; if (ci[connection][display]) { outs << " Connection aborted - display (" << client_id << ") (exceeded max number of send attempts)\n"; } else { outs << " Connection aborted - client (" << client_id << ") (exceeded max number of send attempts)\n"; } } } } ///------------------------------------------------------------- function_body Integer Local_View ( preserves World& w, preserves Integer spec, preserves Integer col, preserves Integer row, preserves Integer dir ) { object Integer local_view; object Integer next_cell, next_spec, next_col, next_row, next_dir; Geometry::Get_Next_Position (col, row, dir, next_col, next_row); next_cell = w.Cell_Content (next_col, next_row); if (next_cell == world_empty_cell) { local_view = EMPTY; } else if (next_cell == world_wall_cell) { local_view = WALL; } else { w.Get_Bug_Species (next_cell, next_spec); if (next_spec == spec) { local_view = FRIEND; } else { local_view = ENEMY; } } return local_view; } ///------------------------------------------------------------- procedure_body Make_Forward_Connection ( preserves Integer client_id, alters Client_Info& ci ) { object Character_OStream os; object Text msg; os.Open_External (ci[connection][host], ci[connection][port]); if (os.Is_Open ()) { ci[state] = identify; ci[io][to] &= os; } else if (ci[io][from].At_EOS ()) { ci[state] = dead; if (ci[connection][display]) { outs << " Connection aborted by client - display (" << client_id << ")\n"; } else { outs << " Connection aborted by client - client (" << client_id << ")\n"; } } else { ci[connection][connect_attempts]++; if (ci[connection][connect_attempts] == MAX_CONNECTION_ATTEMPTS) { ci[state] = dead; if (ci[connection][display]) { outs << " Connection aborted - display (" << client_id << ") (exceeded max number of connection attempts)\n"; } else { outs << " Connection aborted - client (" << client_id << ") (exceeded max number of connection attempts)\n"; } } } } ///------------------------------------------------------------- procedure_body Process_Arguments ( preserves Command_Line_Handler& command_line ) { object Integer i = 1; object Boolean error; columns = 15; rows = 15; initial_number_of_bugs = 10; max_number_of_clients = 10; max_number_of_displays = 10; // initialize random number generators col_gen.Set_Value (2345); row_gen.Set_Value (3456); dir_gen.Set_Value (4567); while ((i < command_line.Number_Of_Arguments ()) and not error) { if (command_line[i] == "-h") // help { error = true; // a trick to get out and print help message } else if (command_line[i] == "-c") // columns { i++; if (i < command_line.Number_Of_Arguments ()) { if (Can_Convert_To_Integer (command_line[i])) { columns = To_Integer (command_line[i]); if ((columns > 0) and (columns <= MAX_WORLD_SIZE)) { i++; } else { serr << "Violated condition: 0 < columns <= " << MAX_WORLD_SIZE << "\n"; error = true; } } else { serr << "Invalid argument: columns = " << command_line[i] << "\n"; error = true; } } else { serr << "Missing argument: columns\n"; error = true; } } else if (command_line[i] == "-r") // rows { i++; if (i < command_line.Number_Of_Arguments ()) { if (Can_Convert_To_Integer (command_line[i])) { rows = To_Integer (command_line[i]); if ((rows > 0) and (rows <= MAX_WORLD_SIZE)) { i++; } else { serr << "Violated condition: 0 < rows <= " << MAX_WORLD_SIZE << "\n"; error = true; } } else { serr << "Invalid argument: rows = " << command_line[i] << "\n"; error = true; } } else { serr << "Missing argument: rows\n"; error = true; } } else if (command_line[i] == "-s") // columns and rows { i++; if (i < command_line.Number_Of_Arguments ()) { if (Can_Convert_To_Integer (command_line[i])) { columns = To_Integer (command_line[i]); if ((columns > 0) and (columns <= MAX_WORLD_SIZE)) { rows = columns; i++; } else { serr << "Violated condition: 0 < size <= " << MAX_WORLD_SIZE << "\n"; error = true; } } else { serr << "Invalid argument: size = " << command_line[i] << "\n"; error = true; } } else { serr << "Missing argument: size\n"; error = true; } } else if (command_line[i] == "-b") // max number of bugs { i++; if (i < command_line.Number_Of_Arguments ()) { if (Can_Convert_To_Integer (command_line[i])) { initial_number_of_bugs = To_Integer (command_line[i]); if (initial_number_of_bugs > 0) { i++; } else { serr << "Violated condition: bugs > 0\n"; error = true; } } else { serr << "Invalid argument: bugs = " << command_line[i] << "\n"; error = true; } } else { serr << "Missing argument: bugs\n"; error = true; } } else if (command_line[i] == "-l") // bug life span { i++; if (i < command_line.Number_Of_Arguments ()) { if (Can_Convert_To_Integer (command_line[i])) { max_age = To_Integer (command_line[i]); if (max_age >= 0) { i++; } else { serr << "Violated condition: life >= 0\n"; error = true; } } else { serr << "Invalid argument: life = " << command_line[i] << "\n"; error = true; } } else { serr << "Missing argument: life\n"; error = true; } } else if (command_line[i] == "-m") // max number of client connections { i++; if (i < command_line.Number_Of_Arguments ()) { if (Can_Convert_To_Integer (command_line[i])) { max_number_of_clients = To_Integer (command_line[i]); if (max_number_of_clients > 0) { i++; } else { serr << "Violated condition: clients > 0\n"; error = true; } } else { serr << "Invalid argument: clients = " << command_line[i] << "\n"; error = true; } } else { serr << "Missing argument: clients\n"; error = true; } } else if (command_line[i] == "-d") // max number of display connections { i++; if (i < command_line.Number_Of_Arguments ()) { if (Can_Convert_To_Integer (command_line[i])) { max_number_of_displays = To_Integer (command_line[i]); if (max_number_of_displays > 0) { i++; } else { serr << "Violated condition: displays > 0\n"; error = true; } } else { serr << "Invalid argument: displays = " << command_line[i] << "\n"; error = true; } } else { serr << "Missing argument: displays\n"; error = true; } } else if (command_line[i] == "-g") // random number generator seed { i++; if (i < command_line.Number_Of_Arguments ()) { if (Can_Convert_To_Integer (command_line[i])) { object Integer seed = To_Integer (command_line[i]); if (seed >= 0) { col_gen.Set_Value (seed + 1111); row_gen.Set_Value (seed + 2222); dir_gen.Set_Value (seed + 3333); i++; } else { serr << "Violated condition: seed >= 0\n"; error = true; } } else { serr << "Invalid argument: seed = " << command_line[i] << "\n"; error = true; } } else { serr << "Missing argument: seed\n"; error = true; } } else if (command_line[i] == "-td") // tournament delay { i++; if (i < command_line.Number_Of_Arguments ()) { if (Can_Convert_To_Integer (command_line[i])) { tournament_delay = To_Integer (command_line[i]); if (tournament_delay >= 0) { i++; if (tournament_duration == 0) { tournament_duration = 1000; } } else { serr << "Violated condition: delay >= 0\n"; error = true; } } else { serr << "Invalid argument: delay = " << command_line[i] << "\n"; error = true; } } else { serr << "Missing argument: delay\n"; error = true; } } else if (command_line[i] == "-tl") // tournament duration (length) { i++; if (i < command_line.Number_Of_Arguments ()) { if (Can_Convert_To_Integer (command_line[i])) { tournament_duration = To_Integer (command_line[i]); if (tournament_duration >= 0) { i++; if (tournament_delay == 0) { tournament_delay = 1000; } } else { serr << "Violated condition: length >= 0\n"; error = true; } } else { serr << "Invalid argument: length = " << command_line[i] << "\n"; error = true; } } else { serr << "Missing argument: length\n"; error = true; } } else { serr << "Invalid argument: " << command_line[i] << "\n"; error = true; } if (error) { serr << "usage: server [ -h ] [ -td delay ] [ -tl length ]\n"; serr << " [ -m clients ] [ -d displays ]\n"; serr << " [ -c columns ] [ -r rows ] [ -s size ]\n"; serr << " [ -b bugs ] [ -l life ] [ -g seed ]\n"; serr << "\nOptions:\n"; serr << " -h - prints this message\n"; serr << " -td delay " << "- delay to run in tournament mode (default 0/1000)\n"; serr << " -tl length " << "- duration of tournament mode (default 0/1000)\n"; serr << " -m clients " << "- max number of client connections (default 10)\n"; serr << " -d displays " << "- max number of display connections (default 10)\n"; serr << " -c columns - world width (default 15)\n"; serr << " -r rows - world height (default 15)\n"; serr << " -s size - world width and height (default 15)\n"; serr << " -b bugs - number of new bugs (default 10)\n"; serr << " -l life - bug's life span (default 0," << " i.e., no limit)\n"; serr << " -g seed - random number generator seed\n"; exit (-1); } } max_number_of_bugs = columns * rows; } ///------------------------------------------------------------- procedure_body Process_Client_Requests ( alters World& w, alters Client_Map& cm, consumes Request_Scheduler& rs, produces Message_Queue& messages ) { object Request_Info ri; object Integer id, spec, col, row, dir, age; object Message_Info msg; messages.Clear (); rs.Change_To_Extraction_Phase (); while (rs.Size () > 0) { rs.Remove_Random (ri); // if (ri[action] != SKIP) // { w.Remove_Bug (ri[bug_id], spec, col, row, dir, age); if (cm.Is_Defined (ri[species_id])) { if (spec == ri[species_id]) { case_select (ri[action]) { case MOVE: { if (Local_View (w, spec, col, row, dir) == EMPTY) { object Integer next_col, next_row; Geometry::Get_Next_Position ( col, row, dir, next_col, next_row); col = next_col; row = next_row; } } break; case TURNLEFT: { dir = Geometry::Get_Next_Direction_Left (dir); } break; case TURNRIGHT: { dir = Geometry::Get_Next_Direction_Right (dir); } break; case INFECT: { if (Local_View (w, spec, col, row, dir) == ENEMY) { object Integer next_col, next_row, next_bug, next_spec, next_dir, next_age; Geometry::Get_Next_Position ( col, row, dir, next_col, next_row); next_bug = w.Cell_Content (next_col, next_row); w.Remove_Bug (next_bug, next_spec, next_col, next_row, next_dir, next_age); w.Add_Bug (next_bug, spec, next_col, next_row, next_dir, 0); cm[spec][bug_count]++; age++; if (cm.Is_Defined (next_spec)) { cm[next_spec][bug_count]--; msg[msg_species_id] = next_spec; msg[msg_bug_id] = next_bug; msg[status] = DEATH; messages.Enqueue (msg); } msg[msg_species_id] = spec; msg[msg_bug_id] = next_bug; msg[msg_col] = next_col; msg[msg_row] = next_row; msg[msg_dir] = next_dir; msg[status] = BIRTH; messages.Enqueue (msg); } } break; default: break; } msg[msg_species_id] = spec; msg[msg_bug_id] = ri[bug_id]; msg[msg_col] = col; msg[msg_row] = row; msg[msg_dir] = dir; msg[status] = ACTIVE; messages.Enqueue (msg); } } w.Add_Bug (ri[bug_id], spec, col, row, dir, age); // } } rs.Clear (); } ///------------------------------------------------------------- procedure_body Read_Line_From_Client ( alters Character_IStream& is, alters Text& buf, preserves Integer max, produces Boolean& eol, produces Boolean& eos ) { object Integer count; object Character ch; eol = false; while (is.OK_To_Read () and (count < max)) { is.Read (ch); if (ch != '\n') { buf.Add (buf.Length (), ch); count++; } else { eol = true; break; } } eos = is.At_EOS (); } ///------------------------------------------------------------- procedure_body Scan_World ( alters World& w, alters Client_Map& cm, alters Message_Queue& client_msgs, produces Text& display_msg ) { object World tmp_world; object Integer id, spec, col, row, dir, age; object Message_Info msg; display_msg.Clear (); // check each bug's age, and send death certificate if necessary tmp_world.Set_Bounds (columns, rows); while (w.Number_Of_Bugs () > 0) { w.Remove_Any_Bug (id, spec, col, row, dir, age); display_msg.Add (display_msg.Length (), To_Character (col + 32)); display_msg.Add (display_msg.Length (), To_Character (row + 32)); display_msg.Add (display_msg.Length (), To_Character (4 * spec + dir + 32)); if ((max_age == 0) or (age < max_age)) { tmp_world.Add_Bug (id, spec, col, row, dir, age + 1); } else { if (cm.Is_Defined (spec)) { cm[spec][bug_count]--; number_of_bugs--; msg[msg_species_id] = spec; msg[msg_bug_id] = id; msg[status] = DEATH; client_msgs.Enqueue (msg); } } } w &= tmp_world; } ///------------------------------------------------------------- procedure_body Send ( preserves Integer client_id, alters Client_Info& ci, preserves Integer time ) { object Text msg; if (ci[io][to].OK_To_Write ()) { ci[io][to] << ci[io][outbuffer]; ci[state] = connected; ci[io][outbuffer].Clear (); ci[connection][connect_attempts] = 0; } else if (ci[io][from].At_EOS ()) { ci[state] = dead; number_of_clients--; outs << " Connection closed by client - client (" << client_id << "/" << time << ")\n"; } else { ci[connection][connect_attempts]++; if (ci[connection][connect_attempts] == MAX_CONNECTION_ATTEMPTS) { ci[state] = dead; number_of_clients--; outs << " Connection closed - client (" << client_id << "/" << time << ") (exceeded max number of send attempts)\n"; } } } ///------------------------------------------------------------- procedure_body Send_World_Size ( preserves Integer client_id, alters Client_Info& ci, preserves Integer time ) { object Text msg; if (ci[io][to].OK_To_Write ()) { ci[io][to] << columns << "\n" << rows << "\n"; ci[state] = connected; ci[connection][connect_attempts] = 0; } else if (ci[io][from].At_EOS ()) { ci[state] = dead; number_of_displays--; outs << " Connection closed by client - display (" << client_id << "/" << time << ")\n"; } else { ci[connection][connect_attempts]++; if (ci[connection][connect_attempts] == MAX_CONNECTION_ATTEMPTS) { ci[state] = dead; number_of_displays--; outs << " Connection closed - display (" << client_id << "/" << time << ") (exceeded max number of send attempts)\n"; } } } ///------------------------------------------------------------- /// Interface -------------------------------------------------- ///------------------------------------------------------------- program_body main (end_user_command_line) { object Client_Map clients, displays; object Command_Line_Handler command_line; object World world; object Integer count; object Text response; // process command line arguments and initialize control objects command_line.Get_Command_Line (command_line_arguments); Process_Arguments (command_line); // open stdin and stdout streams ins.Open_External (""); outs.Open_External (""); Advertise_Server_Host_And_Port (); while (true) { Do_One_Iteration (world, clients, displays, count); count++; if (tournament_duration > 0) { if ((count == (tournament_delay + tournament_duration)) or ((clients.Size () <= 1) and (count > tournament_delay))) { while (clients.Size () > 0) { object Integer d; object Client_Info r; clients.Undefine_Any (d, r); outs << "Client <" << d << "> --> " << r[bug_count] << " bugs left\n"; if (r[io][from].Is_Open ()) { r[io][from].Close_External (); } if (r[io][to].Is_Open ()) { r[io][to].Close_External (); } } break; } } } outs << "Press Enter to quit "; ins >> response; // Close streams while (displays.Size () > 0) { object Integer d; object Client_Info r; displays.Undefine_Any (d, r); if (r[io][from].Is_Open ()) { r[io][from].Close_External (); } if (r[io][to].Is_Open ()) { r[io][to].Close_External (); } } ins.Close_External (); outs.Close_External (); } ///-------------------------------------------------------------