Skip to content

Commit

Permalink
Reapply "Merge branch 'file-parsing-2' into main"
Browse files Browse the repository at this point in the history
This reverts commit 818c351.
  • Loading branch information
ethanuppal committed May 18, 2024
1 parent 818c351 commit 586287d
Show file tree
Hide file tree
Showing 13 changed files with 325 additions and 41 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "efsw"]
path = efsw
url = https://github.com/SpartanJ/efsw
82 changes: 77 additions & 5 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,79 @@
{
"files.associations": {
"map.h": "cpp"
},
"C_Cpp.formatting": "clangFormat",
"C_Cpp.clang_format_style": "file"
"files.associations": {
"map.h": "cpp",
"optional": "cpp",
"fstream": "cpp",
"cctype": "cpp",
"clocale": "cpp",
"cmath": "cpp",
"cstdarg": "cpp",
"cstddef": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"cstring": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"cwctype": "cpp",
"array": "cpp",
"atomic": "cpp",
"bit": "cpp",
"*.tcc": "cpp",
"bitset": "cpp",
"charconv": "cpp",
"chrono": "cpp",
"codecvt": "cpp",
"compare": "cpp",
"concepts": "cpp",
"condition_variable": "cpp",
"cstdint": "cpp",
"deque": "cpp",
"map": "cpp",
"set": "cpp",
"string": "cpp",
"unordered_map": "cpp",
"vector": "cpp",
"exception": "cpp",
"algorithm": "cpp",
"functional": "cpp",
"iterator": "cpp",
"memory": "cpp",
"memory_resource": "cpp",
"numeric": "cpp",
"random": "cpp",
"ratio": "cpp",
"regex": "cpp",
"source_location": "cpp",
"string_view": "cpp",
"system_error": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"utility": "cpp",
"format": "cpp",
"initializer_list": "cpp",
"iomanip": "cpp",
"iosfwd": "cpp",
"iostream": "cpp",
"istream": "cpp",
"limits": "cpp",
"mutex": "cpp",
"new": "cpp",
"numbers": "cpp",
"ostream": "cpp",
"semaphore": "cpp",
"span": "cpp",
"sstream": "cpp",
"stacktrace": "cpp",
"stdexcept": "cpp",
"stop_token": "cpp",
"streambuf": "cpp",
"thread": "cpp",
"cinttypes": "cpp",
"typeinfo": "cpp",
"variant": "cpp",
"cassert": "cpp",
"list": "cpp"
},
"C_Cpp.formatting": "clangFormat",
"C_Cpp.clang_format_style": "file",
"C_Cpp.default.includePath": ["./src"]
}
15 changes: 12 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ INCLUDEDIR := ./src
TESTSDIR := ./tests

CC := $(shell which g++ || which clang)
LDFLAGS := -Lefsw -lefsw
CFLAGS := -std=c++17 -pedantic -Wall -Wextra -I $(INCLUDEDIR)
CDEBUG := -g
CRELEASE := -O2 -DRELEASE_BUILD
Expand All @@ -13,21 +14,29 @@ TARGET := main
SRC := $(shell find $(SRCDIR) -name "*.cpp" -type f)
OBJ := $(SRC:.cpp=.o)

CFLAGS += $(CDEBUG)
.PHONY: build
build: library
make $(TARGET)

$(TARGET): main.cpp $(OBJ)
$(CC) $(CFLAGS) $^ -o $@
$(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS)

%.o: %.cpp
@echo 'Compiling $@'
$(CC) $(CFLAGS) -MMD -MP $< -c -o $@

.PHONY: library
library:
cmake -DBUILD_SHARED_LIBS=OFF efsw
make -C efsw

.PHONY: clean
clean:
rm -rf $(OBJ) $(TARGET) $(shell find . -name "*.dSYM")
make -C efsw clean

.PHONY: run
run: $(TARGET) $(OBJ)
run: build
./$(TARGET)

.PHONY: test
Expand Down
14 changes: 9 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,21 @@ The idea is that a computer can process much more than a human brain can.
For example, when one player kills another, it is likely that it was in hand-to-hand combat.
If a probability distribution of player locations is maintained, we can update them to reflect this new information.

## Usage
## Usage/Building

You can use this project through the `Makefile`.
You must have C++17.

```bash
make # builds the ./main script
make run # builds and runs the ./main script
make clean # removes auxillary and executable files
make test # runs the tests
make # builds the ./main script
make run # builds and runs the ./main script
make library # builds efsw (used for file watching)
make clean # removes auxillary and executable files
make test # runs the tests
```

`cmake` > 3.15 is required to build efsw.

`make run` will run a testing script for the latest model.
You can see all models in [`src/models/`](./src/models/).
Developing a new model is as simple as implementing the [`GameDelegate`](./src/game/gamedelegate.h) interface.
8 changes: 8 additions & 0 deletions data/example_chat.htm
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<code><span>[08:25:06] </span><span></span><span style="color: #FF5;"></span><span></span><span style="color: #AAA;">player3</span><span></span><span></span><span></span><span style="color: #FF5;"> has joined (</span><span></span><span style="color: #5FF;">7</span><span></span><span></span><span></span><span style="color: #FF5;">/</span><span></span><span style="color: #5FF;">8</span><span></span><span></span><span></span><span style="color: #FF5;">)!</span><span></span><span style="color: #FF5;"></span><span></span></code><br>
<code><span>[08:26:50] </span><span></span><span style="color: #5FF;">+25 Bed Wars Experience (Time Played)</span><span></span></code><br>
<code><span>[08:28:21] </span><span></span><span style="color: #F5F;">player1 </span><span></span><span style="color: #AAA;">fell into the void.</span><span></span></code><br>
<code><span>[08:27:02] </span><span></span><span style="color: #000;">BED DESTRUCTION &gt; </span><span></span><span style="color: #AAA;"></span><span></span><span style="color: #F55;">Red </span><span></span><span style="color: #F55;">Bed </span><span></span><span style="color: #AAA;">was dismantled by </span><span></span><span style="color: #55F;">player4</span><span></span><span style="color: #AAA;">!</span><span></span></code><br>
<code><span>[08:28:05] </span><span></span><span style="color: #5FF;">player2 </span><span></span><span style="color: #AAA;">met their end by </span><span></span><span style="color: #000;">player5</span><span></span><span style="color: #AAA;">.</span><span></span></code><br>
<code><span>[08:27:44] </span><span></span><span style="color: #5F5;">player3 </span><span></span><span style="color: #AAA;">was trampled by </span><span></span><span style="color: #FF5;">player6</span><span></span><span style="color: #AAA;">.</span><span></span></code><br>
<code><span>[08:27:02] </span><span></span><span style="color: #55F;">player4 </span><span></span><span style="color: #AAA;">was killed by </span><span></span><span style="color: #F55;">player7</span><span></span><span style="color: #AAA;">.</span><span></span></code><br>
<code><span>[08:27:43] </span><span></span><span style="color: #F55;">player7 </span><span></span><span style="color: #AAA;">was given the cold shoulder by </span><span></span><span style="color: #55F;">player4</span><span></span><span style="color: #AAA;">. </span><span></span><span style="color: #5FF;">FINAL KILL!</span><span></span></code><br>
1 change: 1 addition & 0 deletions efsw
Submodule efsw added at 3b0ffd
74 changes: 56 additions & 18 deletions main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,47 @@

#include <iostream>
#include <cassert>
#include <fstream>
#include "bwmodel.h"
#include "efsw/include/efsw/efsw.hpp"
using namespace bwmodel;
namespace fs = std::filesystem;

class FileWatcherListener : public efsw::FileWatchListener {
Game& game;
const std::filesystem::path& filename;
std::ifstream stream;

public:
FileWatcherListener(Game& game, const fs::path& dir, const fs::path& file)
: game(game), filename(file) {
stream.open(dir / file);
if (stream.fail()) throw std::runtime_error("Failed to open file");

stream.seekg(0, std::ios::end);
}

~FileWatcherListener() {
stream.close();
}

void handleFileAction(efsw::WatchID watchid, const std::string& dir,
const std::string& filename, efsw::Action action,
std::string oldFilename) override {
if (action != efsw::Action::Modified || filename != this->filename)
return;

stream.clear();

std::string line;
while (std::getline(stream, line)) {
auto event = parse_line(line);
if (event.has_value()) {
event.value()(game);
}
}
}
};

int main() {
try {
Expand All @@ -16,25 +55,24 @@ int main() {
std::shared_ptr<GameDelegate> model = Model::v1::make();
game.attach(model);

Simulator simulator;

// we are about to POP OFF
int off = 1;
for (int i = 0; i < PLAYER_COUNT; i++) {
if (i != *me) {
int local_i = i;
simulator.sequence(500 * (i + off), [me, local_i](Game& game) {
game.notify_break_bed(me,
static_cast<PlayerColor>(local_i));
game.notify_player_kill(me,
static_cast<PlayerColor>(local_i));
});
} else {
off--;
}
}
std::cout << "Chat log directory: ";
std::string path;
std::getline(std::cin, path);

std::cout << "Chat log file name: ";
std::string name;
std::getline(std::cin, name);

efsw::FileWatcher watcher;
FileWatcherListener listener(game, path, name);

watcher.addWatch(path, &listener, false);

watcher.watch();

simulator.simulate(game);
std::cout << "Watching for changes...\n";
std::cout << "Press enter to exit.\n";
std::cin.get();
} catch (const MapLoadError& error) {
std::cerr << "MapLoadError: " << error.what() << '\n';
return 1;
Expand Down
1 change: 1 addition & 0 deletions src/bwmodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@
#include "game/sim.h"
#include "models/v1.h"
#include "util/logger.h"
#include "chat/parseline.h"
96 changes: 96 additions & 0 deletions src/chat/parseline.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
#include "parseline.h"
#include <regex>
#include <algorithm>
#include "game/player.h"

namespace bwmodel {
// TODO: incomplete/untested
std::optional<PlayerColor> color_from_str(const std::string& str) {
if (str == "000") return PlayerColor::BLACK;
if (str == "55F") return PlayerColor::BLUE;
if (str == "5F5") return PlayerColor::GREEN;
if (str == "5FF") return PlayerColor::AQUA;
if (str == "F55") return PlayerColor::RED;
if (str == "F5F") return PlayerColor::PINK;
if (str == "FF5") return PlayerColor::YELLOW;
if (str == "AAA") return PlayerColor::WHITE;
return {};
}

std::vector<ColorSection> parse_color_sections(const std::string& line) {
std::regex color_regex("<span style=\"color: #(.*?);\">(.*?)<\\/span>",
std::regex_constants::ECMAScript);
std::smatch matches;

std::vector<ColorSection> sections;

// StackOverflow Answer: https://stackoverflow.com/a/35026140
std::string::const_iterator search_start(line.cbegin());
while (std::regex_search(search_start, line.cend(), matches,
color_regex)) {
assert(matches.size() == 3);
sections.push_back({matches[1], matches[2]});
search_start = matches.suffix().first;
}

return sections;
}

bool is_white_space(const std::string& str) {
return std::all_of(str.cbegin(), str.cend(),
[](char c) { return std::isspace(c); });
}

bool is_game_start(const std::vector<ColorSection>& sections) {
return sections.size() == 2 && sections[0].color == "000"
&& is_white_space(sections[0].text) && sections[1].color == "000"
&& sections[1].text == "Bed Wars";
}

bool is_bed_destruction(const std::vector<ColorSection>& sections) {
return sections.size() == 7 && sections[0].color == "000"
&& sections[0].text.find("BED DESTRUCTION") != std::string::npos;
}

bool is_kill(const std::vector<ColorSection>& sections) {
return sections.size() == 4
|| (sections.size() == 5 && sections[4].text == "FINAL KILL!");
}

std::optional<Event> parse_line(const std::string& line) {
auto sections = parse_color_sections(line);

if (is_game_start(sections)) {
return [](Game& game) { game.notify_start(); };
}

if (is_bed_destruction(sections)) {
auto opt_bed_color = color_from_str(sections[2].color);
auto opt_player_color = color_from_str(sections[5].color);

if (!opt_bed_color.has_value() || !opt_player_color.has_value()) {
return {};
}

return [opt_bed_color, opt_player_color](Game& game) {
game.notify_break_bed(opt_player_color.value(),
opt_bed_color.value());
};
}

if (is_kill(sections)) {
auto opt_killed = color_from_str(sections[0].color);
auto opt_killer = color_from_str(sections[2].color);

if (!opt_killed.has_value() || !opt_killer.has_value()) {
return {};
}

return [opt_killed, opt_killer](Game& game) {
game.notify_player_kill(opt_killer.value(), opt_killed.value());
};
}

return {};
}
}
14 changes: 14 additions & 0 deletions src/chat/parseline.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#include <optional>
#include <functional>
#include "game/game.h"

namespace bwmodel {
using Event = std::function<void(Game&)>;

struct ColorSection {
std::string color;
std::string text;
};

std::optional<Event> parse_line(const std::string& line);
}
Loading

0 comments on commit 586287d

Please sign in to comment.