protohackers

My solutions to the protohackers.com challenges.
git clone git://henryandlizzy.uk/protohackers
Log | Files | Refs

protohack-3.cpp (2883B)


      1 #include "inet.hpp"
      2 
      3 #include <poll.h>
      4 
      5 #include <iostream>
      6 #include <sstream>
      7 #include <map>
      8 #include <vector>
      9 
     10 #include "fd.hpp"
     11 
     12 using namespace std;
     13 
     14 struct client
     15 {
     16 	descriptor d;
     17 	std::string name, buf;
     18 	stringstream stream;
     19 
     20 	bool do_read()
     21 	{
     22 		string data = read(d);
     23 		if (data.empty())
     24 			return false;
     25 
     26 		buf.append(data);
     27 		return true;
     28 	}
     29 	string getline()
     30 	{
     31 		auto s = buf.find_first_not_of('\n');
     32 		auto n = buf.find('\n', s);
     33 		if (n == string::npos)
     34 			return {};
     35 
     36 		string line = buf.substr(s, n);
     37 		buf.erase(0, s+n+1);
     38 		return line;
     39 	}
     40 };
     41 
     42 std::map<int, client> unnamed, clients;
     43 
     44 void broadcast(string msg, int exclude)
     45 {
     46 	msg.push_back('\n');
     47 	std::cout << msg;
     48 	for (auto& c : clients)
     49 		if (c.second.d != exclude)
     50 			write(c.second.d, msg);
     51 }
     52 
     53 int main()
     54 {
     55 	auto incoming = inet::listen(SOCK_STREAM);
     56 
     57 	for (;;)
     58 	{
     59 		std::vector<pollfd> awaitables;
     60 		awaitables.push_back({incoming, POLLIN, 0});
     61 		for (auto const& c : unnamed)
     62 			awaitables.push_back({c.second.d, POLLIN, 0});
     63 		for (auto const& c : clients)
     64 			awaitables.push_back({c.second.d, POLLIN, 0});
     65 
     66 		::poll(awaitables.data(), awaitables.size(), INFTIM);
     67 
     68 		for (auto& ev : awaitables)
     69 		{
     70 			if (~ev.revents & POLLIN)
     71 				continue;
     72 
     73 			if (ev.fd == incoming)
     74 			{
     75 				auto conn = inet::accept(incoming);
     76 
     77 				write(conn, "Welcome to H&L-chat, what is your name?\n");
     78 				auto& x = unnamed[conn];
     79 				x.d = move(conn);
     80 				continue;
     81 			}
     82 
     83 			auto it = unnamed.find(ev.fd);
     84 			if (it != unnamed.end())
     85 			{
     86 				auto& c = it->second;
     87 			//	c.stream << read(c.d);
     88 				if (not c.do_read())
     89 				{
     90 					unnamed.erase(it);
     91 					continue;
     92 				}
     93 				string line = c.getline();
     94 				if (line.find_first_not_of("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz") != string::npos)
     95 				{
     96 					unnamed.erase(it);
     97 					continue;
     98 				}
     99 			//	getline(c.stream, line);
    100 				{
    101 					ostringstream msg;
    102 					msg << "* " << line << " joined.";
    103 					broadcast(msg.str(), c.d);
    104 					c.name = line;
    105 				}
    106 				{
    107 					ostringstream msg;
    108 					msg << "* " << clients.size() << " users in chat:";
    109 					for (auto const& o : clients)
    110 						msg << ' ' << o.second.name;
    111 					msg << '\n';
    112 					write(c.d, msg.str());
    113 				}
    114 				clients.insert(unnamed.extract(it));
    115 				continue;
    116 			}
    117 
    118 			it = clients.find(ev.fd);
    119 			if (it != clients.end())
    120 			{
    121 				auto& c = it->second;
    122 			//	string line = read(c.d);
    123 			//	if (line.empty())
    124 				if (not c.do_read())
    125 				{
    126 					ostringstream msg;
    127 					msg << "* " << c.name << " has left the room";
    128 					broadcast(msg.str(), c.d);
    129 					clients.erase(it);
    130 					continue;
    131 				}
    132 
    133 			//	c.stream << line;
    134 
    135 			//	getline(c.stream, line);
    136 				string line = c.getline();
    137 				if (line.empty())
    138 					continue;
    139 				ostringstream msg;
    140 				msg << "[" << c.name << "] " << line;
    141 				broadcast(msg.str(), c.d);
    142 				continue;
    143 			}
    144 
    145 			cerr << "Unknown fd " << ev.fd << endl;
    146 			return 1;
    147 		}
    148 	}
    149 }