Skip to content

Commit

Permalink
okay, some basic functions appear to exist
Browse files Browse the repository at this point in the history
  • Loading branch information
ixchow committed Oct 5, 2023
1 parent 48b57c2 commit d490edd
Show file tree
Hide file tree
Showing 6 changed files with 217 additions and 21 deletions.
145 changes: 132 additions & 13 deletions Scoreboard.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ const uint32_t IdleTimeout = 60;
const uint32_t CloseTimeout = 20;
const uint32_t FindWait = 20;

const float MoveWait = 0.25f;
const float GemWait = 10.0f;
const size_t GemMax = 20;

Scoreboard::Scoreboard(Server &server_, std::vector< std::string > const &maze_, std::vector< Player > const &players_) : maze(maze_), players(players_), server(server_) {
}
Scoreboard::~Scoreboard() {
Expand Down Expand Up @@ -72,8 +76,8 @@ void Scoreboard::update(float elapsed) {
}
const char *as_chars = reinterpret_cast< const char * >(connection->recv_buffer.data());
char role = as_chars[2];
std::string secret = std::string(as_chars + 3, as_chars + 8);
std::string andrewid = std::string(as_chars + 8, as_chars + 2 + length);
std::string secret = std::string(as_chars + 3, as_chars + 9);
std::string andrewid = std::string(as_chars + 9, as_chars + 2 + length);

auto p = std::find_if(players.begin(), players.end(), [&](Player const &player) {
return player.andrewid == andrewid;
Expand All @@ -93,17 +97,21 @@ void Scoreboard::update(float elapsed) {
disconnect(player.gatherer, "Newer gatherer connected.");
}
info.state = ConnectionInfo::Playing;
info.player = &player;
player.gatherer = connection;
player.gatherer_state = Player::PendingView;
player.gatherer_at = random_empty();
player.gatherer_cooldown = MoveWait;
} else if (role == 's') {
if (player.seeker) {
disconnect(player.seeker, "Newer seeker connected.");
}
info.state = ConnectionInfo::Playing;
info.player = &player;
player.seeker = connection;
player.seeker_state = Player::PendingView;
player.seeker_at = random_empty();
player.seeker_cooldown = MoveWait;
} else {
disconnect(connection, "Expecting role to be 'g'atherer or 's'eeker, got '" + std::string(&role, 1) + "' instead.");
break;
Expand Down Expand Up @@ -146,6 +154,7 @@ void Scoreboard::update(float elapsed) {
step(&info.player->gatherer_at, direction);
}
info.player->gatherer_state = Player::PendingView;
info.player->gatherer_cooldown = MoveWait;
} else if (connection == info.player->seeker) {
if (info.player->seeker_state != Player::WaitingMove) {
disconnect(connection, "Don't send a 'M'ove unless you got a 'V'iew.");
Expand All @@ -156,7 +165,8 @@ void Scoreboard::update(float elapsed) {
} else if (direction != glm::ivec2(0)) {
step(&info.player->seeker_at, direction);
}
info.player->gatherer_state = Player::PendingView;
info.player->seeker_state = Player::PendingView;
info.player->seeker_cooldown = MoveWait;
} else {
std::cerr << "Connection marked 'Playing' but isn't gatherer or seeker?" << std::endl;
disconnect(connection, "(Internal server error; sorry!)");
Expand Down Expand Up @@ -202,6 +212,75 @@ void Scoreboard::update(float elapsed) {
}
}
}

//---------------------------------------------------
//game logic (starting new moves etc)

//collect gems:
for (auto &player : players) {
if (player.gatherer && player.gatherer_state != Player::Disconnected) {
auto f = gems.find(player.gatherer_at);
if (f != gems.end()) {
player.gems += 1;
gems.erase(f);

//let them know:
player.gatherer->send_buffer.emplace_back('G');
player.gatherer->send_buffer.emplace_back(0);
}
}
}

//spawn a gem:
gem_cooldown = std::max(0.0f, gem_cooldown - elapsed);
if (gem_cooldown == 0.0f && gems.size() < GemMax) {
gem_cooldown = GemWait;

//find a random spot with no gems, walls, or gatherers:
std::unordered_set< glm::ivec2 > gatherers;
for (auto const &player : players) {
if (player.gatherer && player.gatherer_state != Player::Disconnected) {
gatherers.emplace(player.gatherer_at);
}
}

static std::mt19937 mt(0x31415926);

std::vector< glm::ivec2 > empty;

for (uint32_t y = 0; y < maze.size(); ++y) {
for (uint32_t x = 0; x < maze[y].size(); ++x) {
if (maze[y][x] == ' ') {
if (!gems.count(glm::ivec2(x,y))) {
if (!gatherers.count(glm::ivec2(x,y))) {
empty.emplace_back(x,y);
}
}
}
}
}

if (!empty.empty()) {
gems.emplace(empty[mt() % empty.size()]);
} else {
std::cerr << "No gem spawning -- too full!" << std::endl;
}
}

//ask for new moves if got old moves:
for (auto &player : players) {
player.gatherer_cooldown = std::max(0.0f, player.gatherer_cooldown - elapsed);
player.seeker_cooldown = std::max(0.0f, player.seeker_cooldown - elapsed);

if (player.gatherer && player.gatherer_state == Player::PendingView && player.gatherer_cooldown == 0.0f) {
send_view(player.gatherer, player.gatherer_at);
player.gatherer_state = Player::WaitingMove;
}
if (player.seeker && player.seeker_state == Player::PendingView&& player.seeker_cooldown == 0.0f) {
send_view(player.seeker, player.seeker_at);
player.seeker_state = Player::WaitingMove;
}
}
}

void Scoreboard::draw(glm::uvec2 const &drawable_size) {
Expand Down Expand Up @@ -242,18 +321,20 @@ void Scoreboard::draw(glm::uvec2 const &drawable_size) {
//- - - - - - - -

{ //copy maze to display:

glm::ivec2 maze_ul = glm::ivec2(1, display_size.y - 2);
auto get_cell = [&](glm::ivec2 mz) -> Cell & {
glm::ivec2 px = maze_ul + glm::ivec2(mz.x, -mz.y);
static Cell temp;
if (0 <= px.x && uint32_t(px.x) < display_size.x
&& 0 <= px.y && uint32_t(px.y) < display_size.y) {
return display[px.y * display_size.x + px.x];
} else {
return temp;
}
};
for (uint32_t row = 0; row < maze.size(); ++row) {
glm::ivec2 px;
px.y = maze_ul.y - int32_t(row);
if (!(0 <= px.y && uint32_t(px.y) < display_size.y)) continue;

for (uint32_t col = 0; col < maze[row].size(); ++col) {
px.x = maze_ul.x + int32_t(col);
if (!(0 <= px.x && uint32_t(px.x) < display_size.x)) continue;

Cell &cell = display[px.y * display_size.x + px.x];
Cell &cell = get_cell(glm::ivec2(col, row));
cell.codepoint = maze[row][col];
cell.fg = glm::u8vec3(0x88, 0x88, 0x88);
if (maze[row][col] == ' ') {
Expand All @@ -264,6 +345,24 @@ void Scoreboard::draw(glm::uvec2 const &drawable_size) {

}
}

//players in maze:
for (auto const &player : players) {
if (player.gatherer && player.gatherer_state != Player::Disconnected) {
Cell &cell = get_cell(player.gatherer_at);
cell.codepoint = player.avatar[0];
cell.fg = player.color;
cell.bg = player.background;
}
}

//gems in maze:
for (auto const &gem : gems) {
Cell &cell = get_cell(gem);
cell.codepoint = 0x2666;
cell.bg = glm::u8vec3(0x22, 0x22, 0x44);
cell.fg = glm::u8vec3(0x44, 0xaa, 0xff);
}
}

//- - - - - - - -
Expand Down Expand Up @@ -342,7 +441,9 @@ glm::ivec2 Scoreboard::random_empty() const {
for (uint32_t y = 0; y < maze.size(); ++y) {
for (uint32_t x = 0; x < maze[y].size(); ++x) {
if (maze[y][x] == ' ') {
empty.emplace_back(x,y);
if (!gems.count(glm::ivec2(x,y))) {
empty.emplace_back(x,y);
}
}
}
}
Expand Down Expand Up @@ -447,3 +548,21 @@ void Scoreboard::disconnect(Connection *connection, std::string const &message)
info.idle_time = 0.0f;
}

void Scoreboard::send_view(Connection *connection, glm::ivec2 at) {
auto lookup = [this](glm::ivec2 pos) {
if (0 <= pos.y && size_t(pos.y) < maze.size()) {
if (0 <= pos.x && size_t(pos.x) < maze[pos.y].size()) {
return maze[pos.y][pos.x];
}
}
return '*';
};

connection->send_buffer.emplace_back(uint8_t('V'));
connection->send_buffer.emplace_back(9);
for (int32_t dy = -1; dy <= 1; dy += 1) {
for (int32_t dx = -1; dx <= 1; dx += 1) {
connection->send_buffer.emplace_back(lookup(at + glm::ivec2(dx,dy)));
}
}
}
13 changes: 12 additions & 1 deletion Scoreboard.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
#include <string>
#include <vector>
#include <unordered_map>
#include <unordered_set>

#include <glm/gtx/hash.hpp>

struct Connection;
struct Server;
Expand All @@ -31,6 +34,10 @@ struct Player {
ConnectionState seeker_state = Disconnected;
glm::ivec2 gatherer_at = glm::ivec2(-1);
glm::ivec2 seeker_at = glm::ivec2(-1);

//used to move at lower-than-full-framerate:
float gatherer_cooldown = 0.0f;
float seeker_cooldown = 0.0f;

void disconnect(Connection *); //call to reset gatherer and/or seeker state on disconnect

Expand Down Expand Up @@ -66,17 +73,21 @@ struct Scoreboard : Mode {
virtual void draw(glm::uvec2 const &drawable_size) override;

//helpers:
glm::ivec2 random_empty() const; //random square with no walls in it
glm::ivec2 random_empty() const; //random square with no walls or gems in it
bool step(glm::ivec2 *at, glm::ivec2 step); //step, if possible, in the maze
void declare_found(Player *player); //declare that seeker and gatherer are in the same place

//communication helpers:
void tell(Connection *connection, std::string const &message); //send a 'T'ell message
void disconnect(Connection *connection, std::string const &message); //disconnect a connection with a message
void send_view(Connection *connection, glm::ivec2 at); //send a view given a position

std::vector< std::string > maze;
std::vector< Player > players;

std::unordered_set< glm::ivec2 > gems;
float gem_cooldown = 0.0f;

Server &server;

//track active connections:
Expand Down
66 changes: 66 additions & 0 deletions dist/test-client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@


const name = 'blue';
const secret = 'Secret';

//based on https://gist.github.com/tedmiston/5935757

const net = require('net');

const client = new net.Socket();
client.connect(15466, '127.0.0.1', function() {
console.log('connected');
//send handshake:
const data = `g${secret}${name}`;
client.write('H');
client.write(Buffer.from([data.length]));
client.write(data);
});

let recv = Buffer.from([]);

client.on('data', function(data) {
recv = Buffer.concat([recv, data]);
//console.log('recv is: ' + recv);
while (recv.length >= 2) {
const type = recv.toString('utf8',0,1);
const length = recv[1];

//wait for more data:
if (recv.length < 2 + length) break;

const data = recv.subarray(2, 2+length);

if (type == 'T') {
const message = data.toString();
console.log(`Server said: '${message}'`);
} else if (type == 'V') {
const view = data.toString();
console.assert(view.length === 9);
console.log(`${view[0]}${view[1]}${view[2]}\n${view[3]}${view[4]}${view[5]}\n${view[6]}${view[7]}${view[8]}`);
//random movement:
let moves = [];
if (view[1] == ' ') moves.push('N');
if (view[3] == ' ') moves.push('W');
if (view[5] == ' ') moves.push('E');
if (view[7] == ' ') moves.push('S');

const move = moves[Math.floor(Math.random() * moves.length)];

console.log(`moving ${move}`);

client.write('M');
client.write(Buffer.from([1]));
client.write(move);
} else {
console.log(`Ignoring '${type}' of length ${length}`);
}

//trim the gotten data:
recv = Buffer.from(recv.subarray(2 + length));
}
});

client.on('close', function() {
console.log('server closed connection');
});
2 changes: 1 addition & 1 deletion dist/test-maze.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# ###### #### #
# # # # #
# # ########### #
# # # #
# # #
# ### #### #
# ### ##### # #
# #
Expand Down
4 changes: 2 additions & 2 deletions dist/test-players.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
red secret R #ff0000
green secret G #00ff00
blue secret B #0000ff
green SECRET G #00ff00
blue Secret B #0000ff
8 changes: 4 additions & 4 deletions maze-server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,13 +117,13 @@ int main(int argc, char **argv) {
player.color.g = uint8_t(std::stoi(color.substr(3,2), nullptr, 16));
player.color.b = uint8_t(std::stoi(color.substr(5,2), nullptr, 16));

if (glm::dot(glm::vec3(player.color), glm::vec3(1.0f)) > 255.0f + 127.0f) {
player.background = glm::u8vec3(0x00, 0x00, 0x00);
} else {
if (player.color.r < 127 && player.color.g < 127 && player.color.b < 127) {
player.background = glm::u8vec3(0xff, 0xff, 0xff);
} else {
player.background = glm::u8vec3(0x00, 0x00, 0x00);
}


players.emplace_back(player);
}
}

Expand Down

0 comments on commit d490edd

Please sign in to comment.