From 7a6b8ba60dbd54475e4e7c7a6bdcaf789ad0d9de Mon Sep 17 00:00:00 2001 From: Emanuel Machado da Silva Date: Tue, 25 Feb 2020 17:18:50 -0300 Subject: [PATCH 01/16] [BUILD] Add the run_release Make target --- Makefile | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 2ef70495..8bd030f2 100644 --- a/Makefile +++ b/Makefile @@ -7,15 +7,13 @@ clean: check: build/debug/Makefile cmake --build build/debug --target check -- --no-print-directory +# Debug targets debug: build/debug/Makefile cmake --build build/debug --target puzzles -- --no-print-directory debug_all: build/debug/Makefile cmake --build build/debug -- --no-print-directory -release: build/release/Makefile - cmake --build build/release --target puzzles -- --no-print-directory - run: debug ./build/debug/puzzles @@ -23,7 +21,14 @@ run: debug run_full: debug ./build/debug/puzzles full -.PHONY: all clean check debug debug_all release run +# Release targets +release: build/release/Makefile + cmake --build build/release --target puzzles -- --no-print-directory + +run_release: release + ./build/release/puzzles + +.PHONY: all clean check debug debug_all release run run_release # Specific file targets build/debug/Makefile: CMakeLists.txt From 29dad03a9dcbd2132badb661fa59d11bd32ce8cd Mon Sep 17 00:00:00 2001 From: Emanuel Machado da Silva Date: Tue, 31 Dec 2019 15:29:26 -0300 Subject: [PATCH 02/16] Code Cleanup --- src/shurikens/solver/depth_search_solver.cpp | 2 +- src/shurikens/solver/solver.h | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/shurikens/solver/depth_search_solver.cpp b/src/shurikens/solver/depth_search_solver.cpp index eedf8793..249f7234 100644 --- a/src/shurikens/solver/depth_search_solver.cpp +++ b/src/shurikens/solver/depth_search_solver.cpp @@ -88,5 +88,5 @@ vector DepthSearchSolver::solve(const Shuriken &shuriken, size_t knownUppe } } while (nodes.empty() == false); - return static_cast>(bestSolution); + return static_cast>(bestSolution); } diff --git a/src/shurikens/solver/solver.h b/src/shurikens/solver/solver.h index 8b3eb783..e2a20352 100644 --- a/src/shurikens/solver/solver.h +++ b/src/shurikens/solver/solver.h @@ -30,8 +30,7 @@ #include namespace Shurikens { -class Solver { -public: +struct Solver { Solver(std::string name, u_int8_t quickSolveLimit) : name(std::move(name)), quickSolveLimit(quickSolveLimit) {} virtual ~Solver() = default; From 8ac2fbde22b82ac0a267048bfb35979aedf36b8f Mon Sep 17 00:00:00 2001 From: Emanuel Machado da Silva Date: Tue, 31 Dec 2019 15:29:50 -0300 Subject: [PATCH 03/16] Replace TwoBitStorage with ArbitraryContainer --- CMakeLists.txt | 3 +- ...{twobitstorage.h => arbitrary_container.h} | 45 +++++++++++--- src/common/twobitstorage.cpp | 58 ------------------- .../solver/breadth_search_solver.cpp | 12 ++-- src/shurikens/solver/depth_search_solver.cpp | 14 ++--- src/shurikens/solver/solver.h | 2 + ..._test.cpp => arbitrary_container_test.cpp} | 43 +++++++++----- 7 files changed, 83 insertions(+), 94 deletions(-) rename src/common/{twobitstorage.h => arbitrary_container.h} (53%) delete mode 100644 src/common/twobitstorage.cpp rename tests/common/{twobitstorage_test.cpp => arbitrary_container_test.cpp} (55%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 81d2aa54..4ead005b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,7 +37,6 @@ endif () # Source Files include_directories(src) add_library(puzzles_lib OBJECT - src/common/twobitstorage.cpp src/cpic/data/easy.cpp src/cpic/data/trivial.cpp src/cpic/model/board.cpp @@ -76,8 +75,8 @@ add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND}) add_subdirectory(libs/googletest) set(test_sources + tests/common/arbitrary_container_test.cpp tests/common/numbers_test.cpp - tests/common/twobitstorage_test.cpp tests/cpic/model/cpic_board_builder_test.cpp tests/cpic/model/cpic_board_solver_test.cpp tests/cpic/model/cpic_board_state_test.cpp diff --git a/src/common/twobitstorage.h b/src/common/arbitrary_container.h similarity index 53% rename from src/common/twobitstorage.h rename to src/common/arbitrary_container.h index b90ee331..e1c7d8c3 100644 --- a/src/common/twobitstorage.h +++ b/src/common/arbitrary_container.h @@ -22,24 +22,55 @@ #pragma once +#include #include #include namespace Puzzles { -struct TwoBitStorage { - static const size_t MAX_SIZE = 32; +typedef size_t value_t; - TwoBitStorage(); +template struct ArbitraryContainer { + ArbitraryContainer() : internal(0), _size(0) { + static_assert(MAX_VALUE <= 0b11); + static_assert(MAX_SIZE <= 32); + } + + inline void push(value_t value) { + assert(value <= MAX_VALUE); + assert(_size < MAX_SIZE); + + _size++; + // FIXME: This only works when MAX_VALUE <= 0b11 + auto shift = (MAX_SIZE - _size) * 2; + internal += static_cast(value) << shift; + } + + inline value_t at(size_t i) const { + assert(i < _size); + // FIXME: This only works when MAX_VALUE <= 0b11 + auto shift = (MAX_SIZE - 1 - i) * 2; + return (internal & (static_cast(0b11) << shift)) >> shift; + } + inline value_t operator[](size_t i) const { return at(i); } inline size_t size() const { return _size; } - void push(u_int8_t); - u_int8_t operator[](size_t i) const; - explicit operator std::vector() const; + // TODO: Replace this with real iterators + explicit inline operator std::vector() const { + std::vector result; + result.reserve(_size); + + for (size_t i = 0; i < _size; ++i) { + u_int8_t value = (*this)[i]; + result.push_back(value); + } + + return result; + } private: u_int64_t internal; - u_int8_t _size; + size_t _size; }; } diff --git a/src/common/twobitstorage.cpp b/src/common/twobitstorage.cpp deleted file mode 100644 index fefe7c7f..00000000 --- a/src/common/twobitstorage.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2019 Emanuel Machado da Silva - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "twobitstorage.h" - -#include - -using Puzzles::TwoBitStorage; - -using std::vector; - -TwoBitStorage::TwoBitStorage() : internal(0), _size(0) {} - -void TwoBitStorage::push(u_int8_t value) { - assert(value <= 3); - assert(_size < MAX_SIZE); - - _size++; - auto shift = (MAX_SIZE - _size) * 2; - internal += static_cast(value) << shift; -} - -u_int8_t TwoBitStorage::operator[](size_t i) const { - assert(i < _size); - auto shift = (MAX_SIZE - 1 - i) * 2; - return (internal & (static_cast(3) << shift)) >> shift; -} - -TwoBitStorage::operator std::vector() const { - vector result; - result.reserve(_size); - - for (auto i = 0; i < _size; ++i) { - u_int8_t value = (*this)[i]; - result.push_back(value); - } - - return result; -} diff --git a/src/shurikens/solver/breadth_search_solver.cpp b/src/shurikens/solver/breadth_search_solver.cpp index 6ab85324..5d65f883 100644 --- a/src/shurikens/solver/breadth_search_solver.cpp +++ b/src/shurikens/solver/breadth_search_solver.cpp @@ -22,8 +22,8 @@ #include "breadth_search_solver.h" +#include "common/arbitrary_container.h" #include "common/numbers.h" -#include "common/twobitstorage.h" #include #include @@ -33,20 +33,20 @@ using namespace Puzzles; using namespace Shurikens; -using Puzzles::TwoBitStorage; - using std::move; using std::queue; using std::unordered_set; using std::vector; +typedef Puzzles::ArbitraryContainer MoveStorage; + namespace { struct Node { const Shuriken shuriken; - const TwoBitStorage moves; + const MoveStorage moves; explicit Node(Shuriken shuriken) : shuriken(move(shuriken)), moves() {} - Node(Shuriken shuriken, const TwoBitStorage &moves) : shuriken(move(shuriken)), moves(moves) {} + Node(Shuriken shuriken, const MoveStorage &moves) : shuriken(move(shuriken)), moves(moves) {} }; } @@ -72,7 +72,7 @@ vector BreadthSearchSolver::solve(const Shuriken &shuriken, size_t knownUp for (auto &move : allMoves) { auto newShuriken = next.shuriken.apply(move); if (cache.insert(newShuriken).second) { - TwoBitStorage newMoves(next.moves); + MoveStorage newMoves(next.moves); newMoves.push(move); nodes.emplace(newShuriken, newMoves); diff --git a/src/shurikens/solver/depth_search_solver.cpp b/src/shurikens/solver/depth_search_solver.cpp index 249f7234..21e3bccb 100644 --- a/src/shurikens/solver/depth_search_solver.cpp +++ b/src/shurikens/solver/depth_search_solver.cpp @@ -22,7 +22,7 @@ #include "depth_search_solver.h" -#include "common/twobitstorage.h" +#include "common/arbitrary_container.h" #include #include @@ -31,20 +31,20 @@ using namespace Shurikens; -using Puzzles::TwoBitStorage; - using std::move; using std::stack; using std::unordered_map; using std::vector; +typedef Puzzles::ArbitraryContainer MoveStorage; + namespace { struct Node { const Shuriken shuriken; - const TwoBitStorage moves; + const MoveStorage moves; explicit Node(Shuriken shuriken) : shuriken(move(shuriken)), moves() {} - Node(Shuriken shuriken, const TwoBitStorage &moves) : shuriken(move(shuriken)), moves(moves) {} + Node(Shuriken shuriken, MoveStorage moves) : shuriken(move(shuriken)), moves(move(moves)) {} }; } @@ -56,7 +56,7 @@ vector DepthSearchSolver::solve(const Shuriken &shuriken, size_t knownUppe nodes.emplace(shuriken); // TODO Find a better way of starting with knownUpperBound items - TwoBitStorage bestSolution; + MoveStorage bestSolution; for (size_t i = 0; i <= knownUpperBound; ++i) { bestSolution.push(0); } @@ -79,7 +79,7 @@ vector DepthSearchSolver::solve(const Shuriken &shuriken, size_t knownUppe auto it = cache.find(newShuriken); if (it == cache.end() || it->second > (next.moves.size() + 1)) { - TwoBitStorage newMoves(next.moves); + MoveStorage newMoves(next.moves); newMoves.push(move); cache[newShuriken] = newMoves.size(); diff --git a/src/shurikens/solver/solver.h b/src/shurikens/solver/solver.h index e2a20352..38e2c14c 100644 --- a/src/shurikens/solver/solver.h +++ b/src/shurikens/solver/solver.h @@ -31,6 +31,8 @@ namespace Shurikens { struct Solver { + const static u_int8_t MAX_MOVES = 31; + Solver(std::string name, u_int8_t quickSolveLimit) : name(std::move(name)), quickSolveLimit(quickSolveLimit) {} virtual ~Solver() = default; diff --git a/tests/common/twobitstorage_test.cpp b/tests/common/arbitrary_container_test.cpp similarity index 55% rename from tests/common/twobitstorage_test.cpp rename to tests/common/arbitrary_container_test.cpp index bd3ca5e0..879814d3 100644 --- a/tests/common/twobitstorage_test.cpp +++ b/tests/common/arbitrary_container_test.cpp @@ -20,20 +20,20 @@ * SOFTWARE. */ -#include "common/twobitstorage.h" +#include "common/arbitrary_container.h" #include -using Puzzles::TwoBitStorage; +using Puzzles::ArbitraryContainer; -TEST(TwoBitStorage, ShouldStartAtZero) { - TwoBitStorage storage; +TEST(ArbitraryContainer_0b11_32, ShouldStartAtZero) { + ArbitraryContainer<0b11, 32> storage; EXPECT_EQ(storage.size(), 0); } -TEST(TwoBitStorage, PushToEmpty) { - TwoBitStorage storage; +TEST(ArbitraryContainer_0b11_32, PushToEmpty) { + ArbitraryContainer<0b11, 32> storage; storage.push(3); @@ -41,14 +41,29 @@ TEST(TwoBitStorage, PushToEmpty) { EXPECT_EQ(storage[0], 3); } -TEST(TwoBitStorage, PushMultiple) { - const auto max = TwoBitStorage::MAX_SIZE; - TwoBitStorage storage; +TEST(ArbitraryContainer_0b11_32, PushMultiple) { + const auto maxSize = 32; + ArbitraryContainer<0b11, maxSize> storage; - for (size_t i = 0; i < max; ++i) - storage.push((max - i) % 4); + for (size_t i = 0; i < maxSize; ++i) { + storage.push((maxSize - i) % 0b100); + } - EXPECT_EQ(storage.size(), max); - for (size_t i = 0; i < max; ++i) - EXPECT_EQ(storage[i], (max - i) % 4) << "Comparison failed on position " << i; + EXPECT_EQ(storage.size(), maxSize); + for (size_t i = 0; i < maxSize; ++i) + EXPECT_EQ(storage[i], (maxSize - i) % 0b100) << "Comparison failed on position " << i; +} + +TEST(ArbitraryContainer_0b1_12, PushMultiple) { + const auto maxSize = 12; + ArbitraryContainer<0b1, maxSize> storage; + + for (size_t i = 0; i < maxSize; ++i) { + storage.push((maxSize - 1) % 0b10); + } + + EXPECT_EQ(storage.size(), maxSize); + for (size_t i = 0; i < maxSize; ++i) { + EXPECT_EQ(storage[i], (maxSize - 1) % 0b10) << "Comparison failed on position " << i; + } } From 6b052bb51ab013ed6257caad25c5cded73e60f72 Mon Sep 17 00:00:00 2001 From: Emanuel Machado da Silva Date: Sun, 5 Jan 2020 15:04:39 -0300 Subject: [PATCH 04/16] Make ArbitraryContainer properly generic --- src/common/arbitrary_container.h | 75 +++++++++++++++---- .../solver/breadth_search_solver.cpp | 8 +- src/shurikens/solver/depth_search_solver.cpp | 2 +- src/shurikens/solver/solver.h | 2 - tests/common/arbitrary_container_test.cpp | 64 ++++++++++++---- 5 files changed, 115 insertions(+), 36 deletions(-) diff --git a/src/common/arbitrary_container.h b/src/common/arbitrary_container.h index e1c7d8c3..ac8540e4 100644 --- a/src/common/arbitrary_container.h +++ b/src/common/arbitrary_container.h @@ -23,6 +23,7 @@ #pragma once #include +#include #include #include @@ -30,28 +31,65 @@ namespace Puzzles { typedef size_t value_t; -template struct ArbitraryContainer { +static constexpr inline size_t calculateValueBitLength(size_t value) { + assert(value > 0); + assert(value <= 0b11111111); // No real reason to stop there, I just haven't tested any further - ArbitraryContainer() : internal(0), _size(0) { - static_assert(MAX_VALUE <= 0b11); - static_assert(MAX_SIZE <= 32); + auto digits = 0; + size_t next = 1; + while (value > 0) { + digits++; + + if (next >= value) break; + + value = value - next; + next = next * 2; } + return digits; +} + +template struct ArbitraryContainer { + + const size_t valueBitLength = calculateValueBitLength(MAX_VALUE); + + // Constructors + ArbitraryContainer() = default; + ArbitraryContainer(const ArbitraryContainer&) = default; + inline void push(value_t value) { assert(value <= MAX_VALUE); - assert(_size < MAX_SIZE); + + std::vector results; + while (value > 0) { + results.push_back(value % 2); + value = value / 2; + } + + for (size_t i = 0; i < (valueBitLength - results.size()); ++i) { + internal.push_back(0); + } + + for (auto it = results.rbegin(); it != results.rend(); ++it) { + internal.push_back(*it); + } _size++; - // FIXME: This only works when MAX_VALUE <= 0b11 - auto shift = (MAX_SIZE - _size) * 2; - internal += static_cast(value) << shift; } - inline value_t at(size_t i) const { - assert(i < _size); - // FIXME: This only works when MAX_VALUE <= 0b11 - auto shift = (MAX_SIZE - 1 - i) * 2; - return (internal & (static_cast(0b11) << shift)) >> shift; + inline value_t at(size_t index) const { + assert(index < _size); + + value_t result = 0; + auto start = index * valueBitLength; + + for (size_t i = 0; i < valueBitLength; ++i) { + if (internal[start + i]) { + result += std::pow(2, valueBitLength - 1 - i); + } + } + + return result; } inline value_t operator[](size_t i) const { return at(i); } inline size_t size() const { return _size; } @@ -69,8 +107,15 @@ template struct ArbitraryContainer { return result; } + // Operators + inline ArbitraryContainer &operator=(const ArbitraryContainer &other) { + internal = other.internal; + _size = other._size; + return *this; + } + private: - u_int64_t internal; - size_t _size; + std::vector internal; + size_t _size = 0; }; } diff --git a/src/shurikens/solver/breadth_search_solver.cpp b/src/shurikens/solver/breadth_search_solver.cpp index 5d65f883..736bf187 100644 --- a/src/shurikens/solver/breadth_search_solver.cpp +++ b/src/shurikens/solver/breadth_search_solver.cpp @@ -38,7 +38,7 @@ using std::queue; using std::unordered_set; using std::vector; -typedef Puzzles::ArbitraryContainer MoveStorage; +typedef Puzzles::ArbitraryContainer MoveStorage; namespace { struct Node { @@ -46,13 +46,13 @@ struct Node { const MoveStorage moves; explicit Node(Shuriken shuriken) : shuriken(move(shuriken)), moves() {} - Node(Shuriken shuriken, const MoveStorage &moves) : shuriken(move(shuriken)), moves(moves) {} + Node(Shuriken shuriken, MoveStorage moves) : shuriken(move(shuriken)), moves(move(moves)) {} }; } vector BreadthSearchSolver::solve(const Shuriken &shuriken, size_t knownUpperBound) const { - auto maxNodes = std::pow(allMoves.size(), knownUpperBound) + 1; - auto maxShurikens = static_cast(Numbers::factorial(12)) / 2; + auto maxNodes = static_cast(std::pow(allMoves.size(), knownUpperBound) + 1); + auto maxShurikens = static_cast(std::ceil(Numbers::factorial(12) / 2)); unordered_set cache; cache.reserve(std::min(maxNodes, maxShurikens)); diff --git a/src/shurikens/solver/depth_search_solver.cpp b/src/shurikens/solver/depth_search_solver.cpp index 21e3bccb..5ee62829 100644 --- a/src/shurikens/solver/depth_search_solver.cpp +++ b/src/shurikens/solver/depth_search_solver.cpp @@ -36,7 +36,7 @@ using std::stack; using std::unordered_map; using std::vector; -typedef Puzzles::ArbitraryContainer MoveStorage; +typedef Puzzles::ArbitraryContainer MoveStorage; namespace { struct Node { diff --git a/src/shurikens/solver/solver.h b/src/shurikens/solver/solver.h index 38e2c14c..e2a20352 100644 --- a/src/shurikens/solver/solver.h +++ b/src/shurikens/solver/solver.h @@ -31,8 +31,6 @@ namespace Shurikens { struct Solver { - const static u_int8_t MAX_MOVES = 31; - Solver(std::string name, u_int8_t quickSolveLimit) : name(std::move(name)), quickSolveLimit(quickSolveLimit) {} virtual ~Solver() = default; diff --git a/tests/common/arbitrary_container_test.cpp b/tests/common/arbitrary_container_test.cpp index 879814d3..1d63f6c0 100644 --- a/tests/common/arbitrary_container_test.cpp +++ b/tests/common/arbitrary_container_test.cpp @@ -25,15 +25,34 @@ #include using Puzzles::ArbitraryContainer; +using Puzzles::calculateValueBitLength; -TEST(ArbitraryContainer_0b11_32, ShouldStartAtZero) { - ArbitraryContainer<0b11, 32> storage; +TEST(ArbitraryContainer, ValueBitLength) { + EXPECT_EQ(calculateValueBitLength(0b1), 1); + EXPECT_EQ(calculateValueBitLength(0b10), 2); + EXPECT_EQ(calculateValueBitLength(0b11), 2); + EXPECT_EQ(calculateValueBitLength(0b100), 3); + EXPECT_EQ(calculateValueBitLength(0b111), 3); + EXPECT_EQ(calculateValueBitLength(0b1000), 4); + EXPECT_EQ(calculateValueBitLength(0b1111), 4); + EXPECT_EQ(calculateValueBitLength(0b10000), 5); + EXPECT_EQ(calculateValueBitLength(0b11111), 5); + EXPECT_EQ(calculateValueBitLength(0b100000), 6); + EXPECT_EQ(calculateValueBitLength(0b111111), 6); + EXPECT_EQ(calculateValueBitLength(0b1000000), 7); + EXPECT_EQ(calculateValueBitLength(0b1111111), 7); + EXPECT_EQ(calculateValueBitLength(0b10000000), 8); + EXPECT_EQ(calculateValueBitLength(0b11111111), 8); +} + +TEST(ArbitraryContainer_0b11, ShouldStartAtZero) { + ArbitraryContainer<0b11> storage; EXPECT_EQ(storage.size(), 0); } -TEST(ArbitraryContainer_0b11_32, PushToEmpty) { - ArbitraryContainer<0b11, 32> storage; +TEST(ArbitraryContainer_0b11, PushToEmpty) { + ArbitraryContainer<0b11> storage; storage.push(3); @@ -41,29 +60,46 @@ TEST(ArbitraryContainer_0b11_32, PushToEmpty) { EXPECT_EQ(storage[0], 3); } -TEST(ArbitraryContainer_0b11_32, PushMultiple) { - const auto maxSize = 32; - ArbitraryContainer<0b11, maxSize> storage; +TEST(ArbitraryContainer_0b11, PushMultiple) { + const auto maxSize = 256; + const auto maxValue = 0b11; + ArbitraryContainer storage; for (size_t i = 0; i < maxSize; ++i) { - storage.push((maxSize - i) % 0b100); + storage.push((maxSize - i) % (maxValue + 1)); } EXPECT_EQ(storage.size(), maxSize); for (size_t i = 0; i < maxSize; ++i) - EXPECT_EQ(storage[i], (maxSize - i) % 0b100) << "Comparison failed on position " << i; + EXPECT_EQ(storage[i], (maxSize - i) % (maxValue + 1)) << "Comparison failed on position " << i; +} + +TEST(ArbitraryContainer_0b1, PushMultiple) { + const auto maxSize = 256; + const auto maxValue = 0b1; + ArbitraryContainer storage; + + for (size_t i = 0; i < maxSize; ++i) { + storage.push((maxSize - 1) % (maxValue + 1)); + } + + EXPECT_EQ(storage.size(), maxSize); + for (size_t i = 0; i < maxSize; ++i) { + EXPECT_EQ(storage[i], (maxSize - 1) % (maxValue + 1)) << "Comparison failed on position " << i; + } } -TEST(ArbitraryContainer_0b1_12, PushMultiple) { - const auto maxSize = 12; - ArbitraryContainer<0b1, maxSize> storage; +TEST(ArbitraryContainer_0b11111111, PushMultiple) { + const auto maxSize = 256; + const auto maxValue = 0b11111111; + ArbitraryContainer storage; for (size_t i = 0; i < maxSize; ++i) { - storage.push((maxSize - 1) % 0b10); + storage.push((maxSize - 1) % (maxValue + 1)); } EXPECT_EQ(storage.size(), maxSize); for (size_t i = 0; i < maxSize; ++i) { - EXPECT_EQ(storage[i], (maxSize - 1) % 0b10) << "Comparison failed on position " << i; + EXPECT_EQ(storage[i], (maxSize - 1) % (maxValue + 1)) << "Comparison failed on position " << i; } } From 895e327e2a7aa251b389af2146e1d98993d50cca Mon Sep 17 00:00:00 2001 From: Emanuel Machado da Silva Date: Sun, 21 Jun 2020 15:40:25 -0300 Subject: [PATCH 05/16] Remove unnecessary size variable from ArbitraryContainer --- src/common/arbitrary_container.h | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/common/arbitrary_container.h b/src/common/arbitrary_container.h index ac8540e4..940afbd4 100644 --- a/src/common/arbitrary_container.h +++ b/src/common/arbitrary_container.h @@ -73,12 +73,10 @@ template struct ArbitraryContainer { for (auto it = results.rbegin(); it != results.rend(); ++it) { internal.push_back(*it); } - - _size++; } inline value_t at(size_t index) const { - assert(index < _size); + assert(index < size()); value_t result = 0; auto start = index * valueBitLength; @@ -92,14 +90,14 @@ template struct ArbitraryContainer { return result; } inline value_t operator[](size_t i) const { return at(i); } - inline size_t size() const { return _size; } + inline size_t size() const { return internal.size() / valueBitLength; } // TODO: Replace this with real iterators explicit inline operator std::vector() const { std::vector result; - result.reserve(_size); + result.reserve(size()); - for (size_t i = 0; i < _size; ++i) { + for (size_t i = 0; i < size(); ++i) { u_int8_t value = (*this)[i]; result.push_back(value); } @@ -110,12 +108,10 @@ template struct ArbitraryContainer { // Operators inline ArbitraryContainer &operator=(const ArbitraryContainer &other) { internal = other.internal; - _size = other._size; return *this; } private: std::vector internal; - size_t _size = 0; }; } From 5d3d8ae83d36fa03cfdbc56421f5fecfd455e1fe Mon Sep 17 00:00:00 2001 From: Emanuel Machado da Silva Date: Sun, 5 Jan 2020 20:54:31 -0300 Subject: [PATCH 06/16] Add reserve and shrink_to_fit to ArbitraryStorage --- src/common/arbitrary_container.h | 3 +++ src/shurikens/solver/breadth_search_solver.cpp | 1 + src/shurikens/solver/depth_search_solver.cpp | 2 ++ 3 files changed, 6 insertions(+) diff --git a/src/common/arbitrary_container.h b/src/common/arbitrary_container.h index 940afbd4..f7da2d91 100644 --- a/src/common/arbitrary_container.h +++ b/src/common/arbitrary_container.h @@ -57,6 +57,9 @@ template struct ArbitraryContainer { ArbitraryContainer() = default; ArbitraryContainer(const ArbitraryContainer&) = default; + inline void reserve(size_t new_cap) { internal.reserve(new_cap * valueBitLength); } + inline void shrink_to_fit() { internal.shrink_to_fit(); } + inline void push(value_t value) { assert(value <= MAX_VALUE); diff --git a/src/shurikens/solver/breadth_search_solver.cpp b/src/shurikens/solver/breadth_search_solver.cpp index 736bf187..6164489e 100644 --- a/src/shurikens/solver/breadth_search_solver.cpp +++ b/src/shurikens/solver/breadth_search_solver.cpp @@ -74,6 +74,7 @@ vector BreadthSearchSolver::solve(const Shuriken &shuriken, size_t knownUp if (cache.insert(newShuriken).second) { MoveStorage newMoves(next.moves); newMoves.push(move); + newMoves.shrink_to_fit(); nodes.emplace(newShuriken, newMoves); } diff --git a/src/shurikens/solver/depth_search_solver.cpp b/src/shurikens/solver/depth_search_solver.cpp index 5ee62829..60d0a634 100644 --- a/src/shurikens/solver/depth_search_solver.cpp +++ b/src/shurikens/solver/depth_search_solver.cpp @@ -57,6 +57,7 @@ vector DepthSearchSolver::solve(const Shuriken &shuriken, size_t knownUppe // TODO Find a better way of starting with knownUpperBound items MoveStorage bestSolution; + bestSolution.reserve(knownUpperBound + 1); for (size_t i = 0; i <= knownUpperBound; ++i) { bestSolution.push(0); } @@ -81,6 +82,7 @@ vector DepthSearchSolver::solve(const Shuriken &shuriken, size_t knownUppe if (it == cache.end() || it->second > (next.moves.size() + 1)) { MoveStorage newMoves(next.moves); newMoves.push(move); + newMoves.shrink_to_fit(); cache[newShuriken] = newMoves.size(); nodes.emplace(newShuriken, newMoves); From 7d849dfae85a239db0b8dae2fbb52ab8afe4030e Mon Sep 17 00:00:00 2001 From: Emanuel Machado da Silva Date: Sun, 5 Jan 2020 21:55:55 -0300 Subject: [PATCH 07/16] Use MoveContainer everywhere instead of vectors --- src/common/arbitrary_container.h | 54 +++++++++++++--- src/shurikens/data/data.h | 11 ++-- src/shurikens/data/real.cpp | 26 ++++---- src/shurikens/logger.cpp | 2 +- src/shurikens/logger.h | 4 +- src/shurikens/model.h | 3 + .../solver/breadth_search_solver.cpp | 13 ++-- src/shurikens/solver/breadth_search_solver.h | 2 +- src/shurikens/solver/depth_search_solver.cpp | 16 ++--- src/shurikens/solver/depth_search_solver.h | 2 +- src/shurikens/solver/solver.h | 3 +- tests/common/arbitrary_container_test.cpp | 61 ++++++++++++++----- 12 files changed, 130 insertions(+), 67 deletions(-) diff --git a/src/common/arbitrary_container.h b/src/common/arbitrary_container.h index f7da2d91..735e4b6d 100644 --- a/src/common/arbitrary_container.h +++ b/src/common/arbitrary_container.h @@ -56,10 +56,18 @@ template struct ArbitraryContainer { // Constructors ArbitraryContainer() = default; ArbitraryContainer(const ArbitraryContainer&) = default; + ArbitraryContainer(std::initializer_list values) { + reserve(values.size()); + for (auto value : values) { + push(value); + } + } + // Capacity inline void reserve(size_t new_cap) { internal.reserve(new_cap * valueBitLength); } inline void shrink_to_fit() { internal.shrink_to_fit(); } + // Insertion inline void push(value_t value) { assert(value <= MAX_VALUE); @@ -78,6 +86,7 @@ template struct ArbitraryContainer { } } + // Retrieval inline value_t at(size_t index) const { assert(index < size()); @@ -95,18 +104,31 @@ template struct ArbitraryContainer { inline value_t operator[](size_t i) const { return at(i); } inline size_t size() const { return internal.size() / valueBitLength; } - // TODO: Replace this with real iterators - explicit inline operator std::vector() const { - std::vector result; - result.reserve(size()); + // Iterators + struct const_iterator { + + const_iterator(const ArbitraryContainer *container, size_t position) + : container(container), position(position) {} + + inline bool operator!=(const const_iterator &o) const { return container != o.container || position != o.position; } - for (size_t i = 0; i < size(); ++i) { - u_int8_t value = (*this)[i]; - result.push_back(value); + inline const_iterator *operator++() { + position++; + return this; } - return result; - } + inline value_t operator*() const { + assert(position < container->size()); + return container->at(position); + } + + private: + const ArbitraryContainer *container; + size_t position; + }; + + inline const_iterator begin() const { return const_iterator(this, 0); } + inline const_iterator end() const { return const_iterator(this, size()); } // Operators inline ArbitraryContainer &operator=(const ArbitraryContainer &other) { @@ -114,6 +136,20 @@ template struct ArbitraryContainer { return *this; } + inline bool operator<(const ArbitraryContainer &o) const { + if (size() != o.size()) { + return size() < o.size(); + } + + auto size = internal.size(); + for (size_t i = 0; i < size; ++i) { + if (internal[i] != o.internal[i]) { + return internal[i] < o.internal[i]; + } + } + return false; + } + private: std::vector internal; }; diff --git a/src/shurikens/data/data.h b/src/shurikens/data/data.h index b91ee3af..bf260d7c 100644 --- a/src/shurikens/data/data.h +++ b/src/shurikens/data/data.h @@ -28,29 +28,28 @@ #include #include #include -#include namespace Shurikens { struct ShurikenData { - ShurikenData(std::string name, Shuriken shuriken, const std::vector &solution) + ShurikenData(std::string name, Shuriken shuriken, const Shurikens::MoveContainer &solution) : name(std::move(name)), shuriken(std::move(shuriken)), solutions({solution}) {} - ShurikenData(std::string name, Shuriken shuriken, const std::vector &solution0, - const std::vector &solution1) + ShurikenData(std::string name, Shuriken shuriken, const Shurikens::MoveContainer &solution0, + const Shurikens::MoveContainer &solution1) : name(std::move(name)), shuriken(std::move(shuriken)), solutions({solution0, solution1}) { assert(solution0.size() == solution1.size()); } inline size_t solutionSize() const { return solutions.cbegin()->size(); } - inline bool isSolution(const std::vector &solution) const { + inline bool isSolution(const Shurikens::MoveContainer &solution) const { return solutions.find(solution) != solutions.end(); } const std::string name; const Shuriken shuriken; - const std::set> solutions; + const std::set solutions; }; std::vector createTrivialShurikens(); diff --git a/src/shurikens/data/real.cpp b/src/shurikens/data/real.cpp index 0bcfccf2..c6f0536a 100644 --- a/src/shurikens/data/real.cpp +++ b/src/shurikens/data/real.cpp @@ -28,23 +28,25 @@ using std::vector; inline ShurikenData createReal0() { Shuriken shuriken({C, A, B, D, E, F, G, H, I, J, K, L}); - vector solution0 = {turn_a, swap_top, turn_b, swap_top, turn_a, turn_a, swap_top, turn_a, - swap_top, turn_a, turn_b, turn_b, turn_b, swap_top, turn_a, turn_a, - swap_top, turn_b, turn_b, swap_top, turn_b, turn_b}; - vector solution1 = {turn_a, turn_a, swap_top, turn_b, swap_top, turn_a, turn_a, swap_top, - turn_b, swap_top, turn_a, turn_a, swap_top, turn_b, swap_top, turn_a, - turn_a, swap_top, turn_b, swap_top, turn_a, turn_a}; + Shurikens::MoveContainer solution0 = {turn_a, swap_top, turn_b, swap_top, turn_a, turn_a, swap_top, turn_a, + swap_top, turn_a, turn_b, turn_b, turn_b, swap_top, turn_a, turn_a, + swap_top, turn_b, turn_b, swap_top, turn_b, turn_b}; + Shurikens::MoveContainer solution1 = {turn_a, turn_a, swap_top, turn_b, swap_top, turn_a, turn_a, swap_top, + turn_b, swap_top, turn_a, turn_a, swap_top, turn_b, swap_top, turn_a, + turn_a, swap_top, turn_b, swap_top, turn_a, turn_a}; return ShurikenData("real0", shuriken, solution0, solution1); } inline ShurikenData createReal1() { Shuriken shuriken({B, A, C, D, E, F, G, H, I, J, K, L}); - vector solution0 = {swap_top, turn_a, swap_top, turn_a, turn_a, swap_top, turn_a, turn_b, turn_b, - swap_top, turn_a, swap_top, turn_b, turn_b, swap_top, turn_a, swap_top, turn_a, - turn_b, swap_top, turn_a, turn_a, swap_top, turn_a, swap_top, turn_a, turn_a}; - vector solution1 = {turn_b, turn_a, swap_top, turn_b, turn_b, swap_top, turn_b, swap_top, turn_b, - turn_b, swap_top, turn_b, swap_top, turn_b, turn_b, swap_top, turn_b, turn_a, - turn_a, swap_top, turn_b, swap_top, turn_a, turn_a, swap_top, turn_b, swap_top}; + Shurikens::MoveContainer solution0 = {swap_top, turn_a, swap_top, turn_a, turn_a, swap_top, turn_a, + turn_b, turn_b, swap_top, turn_a, swap_top, turn_b, turn_b, + swap_top, turn_a, swap_top, turn_a, turn_b, swap_top, turn_a, + turn_a, swap_top, turn_a, swap_top, turn_a, turn_a}; + Shurikens::MoveContainer solution1 = {turn_b, turn_a, swap_top, turn_b, turn_b, swap_top, turn_b, + swap_top, turn_b, turn_b, swap_top, turn_b, swap_top, turn_b, + turn_b, swap_top, turn_b, turn_a, turn_a, swap_top, turn_b, + swap_top, turn_a, turn_a, swap_top, turn_b, swap_top}; return ShurikenData("real1", shuriken, solution0, solution1); } diff --git a/src/shurikens/logger.cpp b/src/shurikens/logger.cpp index 503a42db..35a9dd7e 100644 --- a/src/shurikens/logger.cpp +++ b/src/shurikens/logger.cpp @@ -29,7 +29,7 @@ using namespace Shurikens; using std::cout; using std::vector; -void Logger::log(const vector &moves) const { +void Logger::log(const MoveContainer &moves) const { for (const auto &move : moves) { switch (move) { case swap_top: diff --git a/src/shurikens/logger.h b/src/shurikens/logger.h index 85533934..6be5a4fc 100644 --- a/src/shurikens/logger.h +++ b/src/shurikens/logger.h @@ -24,11 +24,9 @@ #include "model.h" -#include - namespace Shurikens { class Logger { public: - void log(const std::vector &) const; + void log(const MoveContainer &) const; }; } diff --git a/src/shurikens/model.h b/src/shurikens/model.h index bbd8f310..d6c3094f 100644 --- a/src/shurikens/model.h +++ b/src/shurikens/model.h @@ -22,6 +22,8 @@ #pragma once +#include "common/arbitrary_container.h" + #include #include @@ -48,6 +50,7 @@ const Cell K = 10; const Cell L = 11; const std::array allMoves = {swap_top, turn_a, turn_b}; +typedef Puzzles::ArbitraryContainer MoveContainer; class Shuriken { public: diff --git a/src/shurikens/solver/breadth_search_solver.cpp b/src/shurikens/solver/breadth_search_solver.cpp index 6164489e..419ae146 100644 --- a/src/shurikens/solver/breadth_search_solver.cpp +++ b/src/shurikens/solver/breadth_search_solver.cpp @@ -22,7 +22,6 @@ #include "breadth_search_solver.h" -#include "common/arbitrary_container.h" #include "common/numbers.h" #include @@ -38,19 +37,17 @@ using std::queue; using std::unordered_set; using std::vector; -typedef Puzzles::ArbitraryContainer MoveStorage; - namespace { struct Node { const Shuriken shuriken; - const MoveStorage moves; + const MoveContainer moves; explicit Node(Shuriken shuriken) : shuriken(move(shuriken)), moves() {} - Node(Shuriken shuriken, MoveStorage moves) : shuriken(move(shuriken)), moves(move(moves)) {} + Node(Shuriken shuriken, MoveContainer moves) : shuriken(move(shuriken)), moves(move(moves)) {} }; } -vector BreadthSearchSolver::solve(const Shuriken &shuriken, size_t knownUpperBound) const { +MoveContainer BreadthSearchSolver::solve(const Shuriken &shuriken, size_t knownUpperBound) const { auto maxNodes = static_cast(std::pow(allMoves.size(), knownUpperBound) + 1); auto maxShurikens = static_cast(std::ceil(Numbers::factorial(12) / 2)); @@ -64,7 +61,7 @@ vector BreadthSearchSolver::solve(const Shuriken &shuriken, size_t knownUp do { auto next = nodes.front(); if (next.shuriken.isSolved()) { - return static_cast>(next.moves); + return next.moves; } nodes.pop(); @@ -72,7 +69,7 @@ vector BreadthSearchSolver::solve(const Shuriken &shuriken, size_t knownUp for (auto &move : allMoves) { auto newShuriken = next.shuriken.apply(move); if (cache.insert(newShuriken).second) { - MoveStorage newMoves(next.moves); + MoveContainer newMoves(next.moves); newMoves.push(move); newMoves.shrink_to_fit(); diff --git a/src/shurikens/solver/breadth_search_solver.h b/src/shurikens/solver/breadth_search_solver.h index 27c66194..d247bceb 100644 --- a/src/shurikens/solver/breadth_search_solver.h +++ b/src/shurikens/solver/breadth_search_solver.h @@ -29,6 +29,6 @@ class BreadthSearchSolver : public Solver { public: BreadthSearchSolver() : Solver("Breadth", 10) {} - std::vector solve(const Shuriken &, size_t knownUpperBound) const override; + MoveContainer solve(const Shuriken &, size_t knownUpperBound) const override; }; } diff --git a/src/shurikens/solver/depth_search_solver.cpp b/src/shurikens/solver/depth_search_solver.cpp index 60d0a634..57c21244 100644 --- a/src/shurikens/solver/depth_search_solver.cpp +++ b/src/shurikens/solver/depth_search_solver.cpp @@ -22,8 +22,6 @@ #include "depth_search_solver.h" -#include "common/arbitrary_container.h" - #include #include #include @@ -36,19 +34,17 @@ using std::stack; using std::unordered_map; using std::vector; -typedef Puzzles::ArbitraryContainer MoveStorage; - namespace { struct Node { const Shuriken shuriken; - const MoveStorage moves; + const MoveContainer moves; explicit Node(Shuriken shuriken) : shuriken(move(shuriken)), moves() {} - Node(Shuriken shuriken, MoveStorage moves) : shuriken(move(shuriken)), moves(move(moves)) {} + Node(Shuriken shuriken, MoveContainer moves) : shuriken(move(shuriken)), moves(move(moves)) {} }; } -vector DepthSearchSolver::solve(const Shuriken &shuriken, size_t knownUpperBound) const { +MoveContainer DepthSearchSolver::solve(const Shuriken &shuriken, size_t knownUpperBound) const { unordered_map cache; cache[shuriken] = 0; @@ -56,7 +52,7 @@ vector DepthSearchSolver::solve(const Shuriken &shuriken, size_t knownUppe nodes.emplace(shuriken); // TODO Find a better way of starting with knownUpperBound items - MoveStorage bestSolution; + MoveContainer bestSolution; bestSolution.reserve(knownUpperBound + 1); for (size_t i = 0; i <= knownUpperBound; ++i) { bestSolution.push(0); @@ -80,7 +76,7 @@ vector DepthSearchSolver::solve(const Shuriken &shuriken, size_t knownUppe auto it = cache.find(newShuriken); if (it == cache.end() || it->second > (next.moves.size() + 1)) { - MoveStorage newMoves(next.moves); + MoveContainer newMoves(next.moves); newMoves.push(move); newMoves.shrink_to_fit(); cache[newShuriken] = newMoves.size(); @@ -90,5 +86,5 @@ vector DepthSearchSolver::solve(const Shuriken &shuriken, size_t knownUppe } } while (nodes.empty() == false); - return static_cast>(bestSolution); + return bestSolution; } diff --git a/src/shurikens/solver/depth_search_solver.h b/src/shurikens/solver/depth_search_solver.h index 869906d9..373b59c5 100644 --- a/src/shurikens/solver/depth_search_solver.h +++ b/src/shurikens/solver/depth_search_solver.h @@ -29,6 +29,6 @@ class DepthSearchSolver : public Solver { public: DepthSearchSolver() : Solver(" Depth", 10) {} - std::vector solve(const Shuriken &, size_t knownUpperBound) const override; + MoveContainer solve(const Shuriken &, size_t knownUpperBound) const override; }; } diff --git a/src/shurikens/solver/solver.h b/src/shurikens/solver/solver.h index e2a20352..195ca00a 100644 --- a/src/shurikens/solver/solver.h +++ b/src/shurikens/solver/solver.h @@ -27,7 +27,6 @@ #include #include #include -#include namespace Shurikens { struct Solver { @@ -37,6 +36,6 @@ struct Solver { const std::string name; const u_int8_t quickSolveLimit; - virtual std::vector solve(const Shuriken &, size_t knownUpperBound) const = 0; + virtual MoveContainer solve(const Shuriken &, size_t knownUpperBound) const = 0; }; } diff --git a/tests/common/arbitrary_container_test.cpp b/tests/common/arbitrary_container_test.cpp index 1d63f6c0..a40d7874 100644 --- a/tests/common/arbitrary_container_test.cpp +++ b/tests/common/arbitrary_container_test.cpp @@ -45,13 +45,22 @@ TEST(ArbitraryContainer, ValueBitLength) { EXPECT_EQ(calculateValueBitLength(0b11111111), 8); } -TEST(ArbitraryContainer_0b11, ShouldStartAtZero) { +TEST(ArbitraryContainer, ShouldStartAtZero) { ArbitraryContainer<0b11> storage; EXPECT_EQ(storage.size(), 0); } -TEST(ArbitraryContainer_0b11, PushToEmpty) { +TEST(ArbitraryContainer, ConstructingWithInitialValues) { + ArbitraryContainer<0b11> storage({0, 1, 2}); + + EXPECT_EQ(storage.size(), 3); + EXPECT_EQ(storage[0], 0); + EXPECT_EQ(storage[1], 1); + EXPECT_EQ(storage[2], 2); +} + +TEST(ArbitraryContainer, PushToEmpty) { ArbitraryContainer<0b11> storage; storage.push(3); @@ -60,36 +69,36 @@ TEST(ArbitraryContainer_0b11, PushToEmpty) { EXPECT_EQ(storage[0], 3); } -TEST(ArbitraryContainer_0b11, PushMultiple) { +TEST(ArbitraryContainer, PushMultiple_0b1) { const auto maxSize = 256; - const auto maxValue = 0b11; + const auto maxValue = 0b1; ArbitraryContainer storage; for (size_t i = 0; i < maxSize; ++i) { - storage.push((maxSize - i) % (maxValue + 1)); + storage.push((maxSize - 1) % (maxValue + 1)); } EXPECT_EQ(storage.size(), maxSize); - for (size_t i = 0; i < maxSize; ++i) - EXPECT_EQ(storage[i], (maxSize - i) % (maxValue + 1)) << "Comparison failed on position " << i; + for (size_t i = 0; i < maxSize; ++i) { + EXPECT_EQ(storage[i], (maxSize - 1) % (maxValue + 1)) << "Comparison failed on position " << i; + } } -TEST(ArbitraryContainer_0b1, PushMultiple) { +TEST(ArbitraryContainer, PushMultiple_0b11) { const auto maxSize = 256; - const auto maxValue = 0b1; + const auto maxValue = 0b11; ArbitraryContainer storage; for (size_t i = 0; i < maxSize; ++i) { - storage.push((maxSize - 1) % (maxValue + 1)); + storage.push((maxSize - i) % (maxValue + 1)); } EXPECT_EQ(storage.size(), maxSize); - for (size_t i = 0; i < maxSize; ++i) { - EXPECT_EQ(storage[i], (maxSize - 1) % (maxValue + 1)) << "Comparison failed on position " << i; - } + for (size_t i = 0; i < maxSize; ++i) + EXPECT_EQ(storage[i], (maxSize - i) % (maxValue + 1)) << "Comparison failed on position " << i; } -TEST(ArbitraryContainer_0b11111111, PushMultiple) { +TEST(ArbitraryContainer, PushMultiple0b11111111) { const auto maxSize = 256; const auto maxValue = 0b11111111; ArbitraryContainer storage; @@ -103,3 +112,27 @@ TEST(ArbitraryContainer_0b11111111, PushMultiple) { EXPECT_EQ(storage[i], (maxSize - 1) % (maxValue + 1)) << "Comparison failed on position " << i; } } + +TEST(ArbitraryContainer, LessThan_SameObjects) { + const ArbitraryContainer<0b11> storage0({0, 1}); + const ArbitraryContainer<0b11> storage1({0, 1}); + + EXPECT_FALSE(storage0 < storage1); + EXPECT_FALSE(storage1 < storage0); +} + +TEST(ArbitraryContainer, LessThan_DifferentSizes) { + const ArbitraryContainer<0b11> storage0({0, 1}); + const ArbitraryContainer<0b11> storage1({0, 1, 2}); + + EXPECT_TRUE(storage0 < storage1); + EXPECT_FALSE(storage1 < storage0); +} + +TEST(ArbitraryContainer, LessThan_DifferentValues) { + const ArbitraryContainer<0b11> storage0({0, 1, 1}); + const ArbitraryContainer<0b11> storage1({0, 1, 2}); + + EXPECT_TRUE(storage0 < storage1); + EXPECT_FALSE(storage1 < storage0); +} From fe926f51db1dab52f2195eba7ad6361a472f0f16 Mon Sep 17 00:00:00 2001 From: Emanuel Machado da Silva Date: Sun, 19 Jan 2020 20:36:34 -0300 Subject: [PATCH 08/16] Make Shuriken::Move and Shuriken::Cell into enums --- src/shurikens/model.h | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/src/shurikens/model.h b/src/shurikens/model.h index d6c3094f..fe758b92 100644 --- a/src/shurikens/model.h +++ b/src/shurikens/model.h @@ -29,25 +29,8 @@ namespace Shurikens { -// Move and Cell should've been enums, but unfortunately this is more space-efficient -typedef uint8_t Move; -const Move swap_top = 0; -const Move turn_a = 1; -const Move turn_b = 2; - -typedef uint8_t Cell; -const Cell A = 0; -const Cell B = 1; -const Cell C = 2; -const Cell D = 3; -const Cell E = 4; -const Cell F = 5; -const Cell G = 6; -const Cell H = 7; -const Cell I = 8; -const Cell J = 9; -const Cell K = 10; -const Cell L = 11; +enum Move : uint8_t { swap_top, turn_a, turn_b }; +enum Cell : uint8_t { A, B, C, D, E, F, G, H, I, J, K, L }; const std::array allMoves = {swap_top, turn_a, turn_b}; typedef Puzzles::ArbitraryContainer MoveContainer; From 5ad13a69ff265f3ad0b08c096509c8cfae19b263 Mon Sep 17 00:00:00 2001 From: Emanuel Machado da Silva Date: Sun, 19 Jan 2020 21:07:06 -0300 Subject: [PATCH 09/16] Add more Shuriken moves --- src/shurikens/data/real.cpp | 24 +++++++--------- src/shurikens/logger.cpp | 9 ++++++ src/shurikens/model.cpp | 36 ++++++++++++++++++++++-- src/shurikens/model.h | 4 +-- tests/shurikens/shurikens_model_test.cpp | 26 ++++++++++++++++- 5 files changed, 80 insertions(+), 19 deletions(-) diff --git a/src/shurikens/data/real.cpp b/src/shurikens/data/real.cpp index c6f0536a..e06b10db 100644 --- a/src/shurikens/data/real.cpp +++ b/src/shurikens/data/real.cpp @@ -28,25 +28,21 @@ using std::vector; inline ShurikenData createReal0() { Shuriken shuriken({C, A, B, D, E, F, G, H, I, J, K, L}); - Shurikens::MoveContainer solution0 = {turn_a, swap_top, turn_b, swap_top, turn_a, turn_a, swap_top, turn_a, - swap_top, turn_a, turn_b, turn_b, turn_b, swap_top, turn_a, turn_a, - swap_top, turn_b, turn_b, swap_top, turn_b, turn_b}; - Shurikens::MoveContainer solution1 = {turn_a, turn_a, swap_top, turn_b, swap_top, turn_a, turn_a, swap_top, - turn_b, swap_top, turn_a, turn_a, swap_top, turn_b, swap_top, turn_a, - turn_a, swap_top, turn_b, swap_top, turn_a, turn_a}; + Shurikens::MoveContainer solution0 = {turn_a, swap_top, turn_b, reverse_a, swap_top, turn_a, swap_top, + reverse_b, swap_top, turn_b, swap_top, reverse_b, swap_top, reverse_a}; + Shurikens::MoveContainer solution1 = {reverse_a, swap_top, reverse_b, swap_top, turn_b, swap_top, reverse_b, + swap_top, turn_a, swap_top, reverse_a, turn_b, swap_top, turn_a}; return ShurikenData("real0", shuriken, solution0, solution1); } inline ShurikenData createReal1() { Shuriken shuriken({B, A, C, D, E, F, G, H, I, J, K, L}); - Shurikens::MoveContainer solution0 = {swap_top, turn_a, swap_top, turn_a, turn_a, swap_top, turn_a, - turn_b, turn_b, swap_top, turn_a, swap_top, turn_b, turn_b, - swap_top, turn_a, swap_top, turn_a, turn_b, swap_top, turn_a, - turn_a, swap_top, turn_a, swap_top, turn_a, turn_a}; - Shurikens::MoveContainer solution1 = {turn_b, turn_a, swap_top, turn_b, turn_b, swap_top, turn_b, - swap_top, turn_b, turn_b, swap_top, turn_b, swap_top, turn_b, - turn_b, swap_top, turn_b, turn_a, turn_a, swap_top, turn_b, - swap_top, turn_a, turn_a, swap_top, turn_b, swap_top}; + Shurikens::MoveContainer solution0 = {swap_top, turn_a, reverse_b, swap_top, turn_a, turn_a, swap_top, + turn_a, turn_a, swap_top, turn_a, swap_top, turn_a, turn_a, + swap_top, turn_b, swap_top, reverse_a, swap_top, turn_a, turn_a}; + Shurikens::MoveContainer solution1 = {reverse_b, reverse_a, swap_top, reverse_b, reverse_b, swap_top, reverse_a, + swap_top, reverse_b, reverse_b, swap_top, reverse_a, reverse_a, swap_top, + reverse_b, turn_a, swap_top, reverse_a, swap_top, turn_b, swap_top}; return ShurikenData("real1", shuriken, solution0, solution1); } diff --git a/src/shurikens/logger.cpp b/src/shurikens/logger.cpp index 35a9dd7e..a7ec6af0 100644 --- a/src/shurikens/logger.cpp +++ b/src/shurikens/logger.cpp @@ -35,12 +35,21 @@ void Logger::log(const MoveContainer &moves) const { case swap_top: cout << "s"; break; + case swap_bottom: + cout << "S"; + break; case turn_a: cout << "a"; break; case turn_b: cout << "b"; break; + case reverse_a: + cout << "A"; + break; + case reverse_b: + cout << "B"; + break; } } cout << "\n"; diff --git a/src/shurikens/model.cpp b/src/shurikens/model.cpp index 120225d9..cfbfa928 100644 --- a/src/shurikens/model.cpp +++ b/src/shurikens/model.cpp @@ -37,6 +37,14 @@ static inline array doSwapTop(array cells) { return cells; } +static inline array doSwapBottom(array cells) { + swap(cells[3], cells[9]); + swap(cells[4], cells[10]); + swap(cells[5], cells[11]); + + return cells; +} + static inline array doInvert(array cells) { swap(cells[0], cells[6]); swap(cells[1], cells[7]); @@ -70,14 +78,38 @@ static inline array doTurnB(const array &cells) { return turned; } +static inline array doReverseA(const array &cells) { + //TODO: Improve this method + auto first = doTurnA(cells); + auto second = doTurnA(first); + auto third = doTurnA(second); + auto fourth = doTurnA(third); + return doTurnA(fourth); +} + +static inline array doReverseB(const array &cells) { + //TODO: Improve this method + auto first = doTurnB(cells); + auto second = doTurnB(first); + auto third = doTurnB(second); + auto fourth = doTurnB(third); + return doTurnB(fourth); +} + Shuriken Shuriken::apply(Shurikens::Move move) const { switch (move) { + case swap_top: + return Shuriken(doSwapTop(this->cells)); + case swap_bottom: + return Shuriken(doSwapBottom(this->cells)); case turn_a: return Shuriken(doTurnA(this->cells)); case turn_b: return Shuriken(doTurnB(this->cells)); - case swap_top: - return Shuriken(doSwapTop(this->cells)); + case reverse_a: + return Shuriken(doReverseA(this->cells)); + case reverse_b: + return Shuriken(doReverseB(this->cells)); default: assert(!"Tried to apply an invalid move!"); return *this; diff --git a/src/shurikens/model.h b/src/shurikens/model.h index fe758b92..4e5c5d7a 100644 --- a/src/shurikens/model.h +++ b/src/shurikens/model.h @@ -29,10 +29,10 @@ namespace Shurikens { -enum Move : uint8_t { swap_top, turn_a, turn_b }; +enum Move : uint8_t { swap_top, swap_bottom, turn_a, turn_b, reverse_a, reverse_b }; enum Cell : uint8_t { A, B, C, D, E, F, G, H, I, J, K, L }; -const std::array allMoves = {swap_top, turn_a, turn_b}; +const std::array allMoves = {swap_top, swap_bottom, turn_a, turn_b, reverse_a, reverse_b}; typedef Puzzles::ArbitraryContainer MoveContainer; class Shuriken { diff --git a/tests/shurikens/shurikens_model_test.cpp b/tests/shurikens/shurikens_model_test.cpp index 43af1b6a..584cd602 100644 --- a/tests/shurikens/shurikens_model_test.cpp +++ b/tests/shurikens/shurikens_model_test.cpp @@ -28,7 +28,7 @@ using namespace Shurikens; using std::array; -TEST(Shuriken, Swap) { +TEST(Shuriken, SwapTop) { auto shuriken = Shuriken({A, B, C, D, E, F, G, H, I, J, K, L}); auto swapped = shuriken.apply(swap_top); @@ -36,6 +36,14 @@ TEST(Shuriken, Swap) { EXPECT_EQ(swapped.cells, cells); } +TEST(Shuriken, SwapBottom) { + auto shuriken = Shuriken({A, B, C, D, E, F, G, H, I, J, K, L}); + auto swapped = shuriken.apply(swap_bottom); + + array cells = {A, B, C, J, K, L, G, H, I, D, E, F}; + EXPECT_EQ(swapped.cells, cells); +} + TEST(Shuriken, TurnSideA) { auto shuriken = Shuriken({A, B, C, D, E, F, G, H, I, J, K, L}); auto swapped = shuriken.apply(turn_a); @@ -52,6 +60,22 @@ TEST(Shuriken, TurnSideB) { EXPECT_EQ(swapped.cells, cells); } +TEST(Shuriken, ReverseSideA) { + auto shuriken = Shuriken({A, B, C, D, E, F, G, H, I, J, K, L}); + auto swapped = shuriken.apply(reverse_a); + + array cells = {B, C, D, E, F, A, G, H, I, J, K, L}; + EXPECT_EQ(swapped.cells, cells); +} + +TEST(Shuriken, ReverseSideB) { + auto shuriken = Shuriken({A, B, C, D, E, F, G, H, I, J, K, L}); + auto swapped = shuriken.apply(reverse_b); + + array cells = {A, B, C, D, E, F, H, I, J, K, L, G}; + EXPECT_EQ(swapped.cells, cells); +} + TEST(Shuriken, PerfectlySolvedShurikenShouldBeSolved) { auto shuriken = Shuriken({A, B, C, D, E, F, G, H, I, J, K, L}); From 6860fd84e5a54720f6de02aad73e85beca51e276 Mon Sep 17 00:00:00 2001 From: Emanuel Machado da Silva Date: Mon, 20 Jan 2020 21:17:36 -0300 Subject: [PATCH 10/16] Optimize Shuriken::apply(Move) --- src/shurikens/model.cpp | 87 ++++++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 41 deletions(-) diff --git a/src/shurikens/model.cpp b/src/shurikens/model.cpp index cfbfa928..5683dc13 100644 --- a/src/shurikens/model.cpp +++ b/src/shurikens/model.cpp @@ -27,73 +27,78 @@ using namespace Shurikens; using std::array; -using std::swap; -static inline array doSwapTop(array cells) { - swap(cells[0], cells[6]); - swap(cells[1], cells[7]); - swap(cells[2], cells[8]); +static inline array doSwapTop(const array &cells) { + array turned = cells; - return cells; + auto cellsBegin = cells.begin(); + auto turnedBegin = turned.begin(); + std::copy(cellsBegin, cellsBegin + 3, turnedBegin + 6); + std::copy(cellsBegin + 6, cellsBegin + 9, turnedBegin); + + return turned; } -static inline array doSwapBottom(array cells) { - swap(cells[3], cells[9]); - swap(cells[4], cells[10]); - swap(cells[5], cells[11]); +static inline array doSwapBottom(const array &cells) { + array turned = cells; + + auto cellsBegin = cells.begin(); + auto turnedBegin = turned.begin(); + std::copy(cellsBegin + 3, cellsBegin + 6, turnedBegin + 9); + std::copy(cellsBegin + 9, cellsBegin + 12, turnedBegin + 3); - return cells; + return turned; } -static inline array doInvert(array cells) { - swap(cells[0], cells[6]); - swap(cells[1], cells[7]); - swap(cells[2], cells[8]); - swap(cells[3], cells[9]); - swap(cells[4], cells[10]); - swap(cells[5], cells[11]); +static inline array doInvert(const array &cells) { + array turned = {}; - return cells; + auto cellsBegin = cells.begin(); + auto turnedBegin = turned.begin(); + std::copy(cellsBegin, cellsBegin + 6, turnedBegin + 6); + std::copy(cellsBegin + 6, cellsBegin + 12, turnedBegin); + + return turned; } static inline array doTurnA(const array &cells) { - array turned = {}; + array turned = cells; - for (int i = 0; i < 6; ++i) { - turned[(i + 1) % 6] = cells[i]; - turned[i + 6] = cells[i + 6]; - } + auto cellsBegin = cells.begin(); + std::copy(cellsBegin, cellsBegin + 5, turned.begin() + 1); + turned[0] = cells[5]; return turned; } static inline array doTurnB(const array &cells) { - array turned = {}; + array turned = cells; - for (int i = 0; i < 6; ++i) { - turned[i] = cells[i]; - turned[((i + 1) % 6) + 6] = cells[i + 6]; - } + auto cellsBegin = cells.begin(); + std::copy(cellsBegin + 6, cellsBegin + 11, turned.begin() + 7); + turned[6] = cells[11]; return turned; } static inline array doReverseA(const array &cells) { - //TODO: Improve this method - auto first = doTurnA(cells); - auto second = doTurnA(first); - auto third = doTurnA(second); - auto fourth = doTurnA(third); - return doTurnA(fourth); + array turned = cells; + + auto cellsBegin = cells.begin(); + std::copy(cellsBegin + 1, cellsBegin + 6, turned.begin()); + turned[5] = cells[0]; + + return turned; } static inline array doReverseB(const array &cells) { - //TODO: Improve this method - auto first = doTurnB(cells); - auto second = doTurnB(first); - auto third = doTurnB(second); - auto fourth = doTurnB(third); - return doTurnB(fourth); + array turned = cells; + + auto cellsBegin = cells.begin(); + std::copy(cellsBegin + 7, cellsBegin + 12, turned.begin() + 6); + turned[11] = cells[6]; + + return turned; } Shuriken Shuriken::apply(Shurikens::Move move) const { From 7d7ec4e7c3af27b2f39e9f72e55873c5978cc6e3 Mon Sep 17 00:00:00 2001 From: Emanuel Machado da Silva Date: Mon, 27 Jan 2020 21:24:42 -0300 Subject: [PATCH 11/16] Optimize Shuriken::operator== --- src/shurikens/model.cpp | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/src/shurikens/model.cpp b/src/shurikens/model.cpp index 5683dc13..35a93225 100644 --- a/src/shurikens/model.cpp +++ b/src/shurikens/model.cpp @@ -50,17 +50,6 @@ static inline array doSwapBottom(const array &cells) { return turned; } -static inline array doInvert(const array &cells) { - array turned = {}; - - auto cellsBegin = cells.begin(); - auto turnedBegin = turned.begin(); - std::copy(cellsBegin, cellsBegin + 6, turnedBegin + 6); - std::copy(cellsBegin + 6, cellsBegin + 12, turnedBegin); - - return turned; -} - static inline array doTurnA(const array &cells) { array turned = cells; @@ -122,9 +111,17 @@ Shuriken Shuriken::apply(Shurikens::Move move) const { } bool Shuriken::operator==(const Shuriken &other) const { - // TODO There must be a better way of doing this check... if (cells == other.cells) return true; - if (cells == doInvert(other.cells)) return true; - return false; + auto us = cells.begin(); + auto them = other.cells.begin(); + + for (auto i = 0; i < 6; ++i) { + if (*us != *(them + 6)) return false; + if (*(us + 6) != *them) return false; + + ++us, ++them; + } + + return true; } From cec70314e3685bc10b9bb8d94b83a0673b0dff4f Mon Sep 17 00:00:00 2001 From: Emanuel Machado da Silva Date: Tue, 25 Feb 2020 16:41:43 -0300 Subject: [PATCH 12/16] Optimize BreadthSearchSolver::solve Do the "is it solved?" check earlier in the process, so we can also finish earlier. To be more precise, we're now need to go one level less deep than before (except for trivial cases, where we're still just as good). --- src/shurikens/solver/breadth_search_solver.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/shurikens/solver/breadth_search_solver.cpp b/src/shurikens/solver/breadth_search_solver.cpp index 419ae146..5d6c23a5 100644 --- a/src/shurikens/solver/breadth_search_solver.cpp +++ b/src/shurikens/solver/breadth_search_solver.cpp @@ -48,6 +48,8 @@ struct Node { } MoveContainer BreadthSearchSolver::solve(const Shuriken &shuriken, size_t knownUpperBound) const { + if (shuriken.isSolved()) return {}; + auto maxNodes = static_cast(std::pow(allMoves.size(), knownUpperBound) + 1); auto maxShurikens = static_cast(std::ceil(Numbers::factorial(12) / 2)); @@ -60,19 +62,20 @@ MoveContainer BreadthSearchSolver::solve(const Shuriken &shuriken, size_t knownU do { auto next = nodes.front(); - if (next.shuriken.isSolved()) { - return next.moves; - } - nodes.pop(); for (auto &move : allMoves) { auto newShuriken = next.shuriken.apply(move); + if (cache.insert(newShuriken).second) { MoveContainer newMoves(next.moves); newMoves.push(move); newMoves.shrink_to_fit(); + if (newShuriken.isSolved()) { + return newMoves; + } + nodes.emplace(newShuriken, newMoves); } } From 7c45ec93c87554619a3c44ccb88ef617b7b11483 Mon Sep 17 00:00:00 2001 From: Emanuel Machado da Silva Date: Wed, 1 Jul 2020 12:17:14 -0300 Subject: [PATCH 13/16] Add a better performant constructor to ArbitraryContainer --- src/common/arbitrary_container.h | 2 ++ src/shurikens/solver/depth_search_solver.cpp | 7 +------ 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/common/arbitrary_container.h b/src/common/arbitrary_container.h index 735e4b6d..ddb91242 100644 --- a/src/common/arbitrary_container.h +++ b/src/common/arbitrary_container.h @@ -56,6 +56,8 @@ template struct ArbitraryContainer { // Constructors ArbitraryContainer() = default; ArbitraryContainer(const ArbitraryContainer&) = default; + explicit ArbitraryContainer(size_t count) : internal(count * valueBitLength, 0) { } + ArbitraryContainer(std::initializer_list values) { reserve(values.size()); for (auto value : values) { diff --git a/src/shurikens/solver/depth_search_solver.cpp b/src/shurikens/solver/depth_search_solver.cpp index 57c21244..067156e9 100644 --- a/src/shurikens/solver/depth_search_solver.cpp +++ b/src/shurikens/solver/depth_search_solver.cpp @@ -51,12 +51,7 @@ MoveContainer DepthSearchSolver::solve(const Shuriken &shuriken, size_t knownUpp stack nodes; nodes.emplace(shuriken); - // TODO Find a better way of starting with knownUpperBound items - MoveContainer bestSolution; - bestSolution.reserve(knownUpperBound + 1); - for (size_t i = 0; i <= knownUpperBound; ++i) { - bestSolution.push(0); - } + MoveContainer bestSolution(knownUpperBound + 1); do { auto next = nodes.top(); From e82edb60bc3ee30c63b9bcea39ef651af9b982d5 Mon Sep 17 00:00:00 2001 From: Emanuel Machado da Silva Date: Thu, 9 Jul 2020 11:05:04 -0300 Subject: [PATCH 14/16] Create a WordRepository for Hangman Also added a helper FileReader class --- CMakeLists.txt | 5 ++++ assets/.gitignore | 2 ++ src/common/file_reader.cpp | 41 ++++++++++++++++++++++++++++++ src/common/file_reader.h | 40 +++++++++++++++++++++++++++++ src/hangman/word_repository.cpp | 45 +++++++++++++++++++++++++++++++++ src/hangman/word_repository.h | 37 +++++++++++++++++++++++++++ src/main.cpp | 25 ++++++++++++++---- 7 files changed, 190 insertions(+), 5 deletions(-) create mode 100644 assets/.gitignore create mode 100644 src/common/file_reader.cpp create mode 100644 src/common/file_reader.h create mode 100644 src/hangman/word_repository.cpp create mode 100644 src/hangman/word_repository.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 4ead005b..033b83b8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,6 +37,7 @@ endif () # Source Files include_directories(src) add_library(puzzles_lib OBJECT + src/common/file_reader.cpp src/cpic/data/easy.cpp src/cpic/data/trivial.cpp src/cpic/model/board.cpp @@ -45,6 +46,7 @@ add_library(puzzles_lib OBJECT src/cpic/solver/brute_force_board_solver.cpp src/cpic/solver/heuristic_board_solver.cpp src/cpic/view/board_logger.cpp + src/hangman/word_repository.cpp src/shurikens/data/easy.cpp src/shurikens/data/real.cpp src/shurikens/data/trivial.cpp @@ -60,6 +62,9 @@ add_library(puzzles_lib OBJECT src/sudoku/view/board_logger.cpp ) +# Assets +file(COPY assets DESTINATION .) + # Executable add_executable(puzzles src/main.cpp $) diff --git a/assets/.gitignore b/assets/.gitignore new file mode 100644 index 00000000..eecd76e5 --- /dev/null +++ b/assets/.gitignore @@ -0,0 +1,2 @@ +words.txt + diff --git a/src/common/file_reader.cpp b/src/common/file_reader.cpp new file mode 100644 index 00000000..04d54e19 --- /dev/null +++ b/src/common/file_reader.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2020 Emanuel Machado da Silva + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "file_reader.h" + +using namespace Puzzles; + +FileReader::FileReader(const std::string &filename) { + input.open(filename); +} + +FileReader::~FileReader() { + input.close(); +} + +std::istream *FileReader::istream() { + if (input) { + return &input; + } else { + return nullptr; + } +} diff --git a/src/common/file_reader.h b/src/common/file_reader.h new file mode 100644 index 00000000..9f3aa948 --- /dev/null +++ b/src/common/file_reader.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2020 Emanuel Machado da Silva + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +#include +#include +#include + +namespace Puzzles { + +struct FileReader { + explicit FileReader(const std::string &filename); + ~FileReader(); + + std::istream *istream(); + +private: + std::fstream input; +}; +} diff --git a/src/hangman/word_repository.cpp b/src/hangman/word_repository.cpp new file mode 100644 index 00000000..e2cd0d21 --- /dev/null +++ b/src/hangman/word_repository.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2020 Emanuel Machado da Silva + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "word_repository.h" + +#include "common/file_reader.h" + +#include + +using namespace Hangman; +using namespace Puzzles; + +std::vector WordRepository::words() { + if (_words.empty()) { + FileReader reader("assets/words.txt"); + auto input = reader.istream(); + if (input) { + std::string buffer; + while (*input >> buffer) { + _words.push_back(buffer); + } + } + } + + return _words; +} diff --git a/src/hangman/word_repository.h b/src/hangman/word_repository.h new file mode 100644 index 00000000..93056bca --- /dev/null +++ b/src/hangman/word_repository.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2018 Emanuel Machado da Silva + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +#include +#include + +namespace Hangman { + +struct WordRepository { + std::vector words(); + +private: + std::vector _words; +}; + +} diff --git a/src/main.cpp b/src/main.cpp index ed39094e..2188ef5f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -24,6 +24,7 @@ #include "cpic/solver/brute_force_board_solver.h" #include "cpic/solver/heuristic_board_solver.h" #include "cpic/view/board_logger.h" +#include "hangman/word_repository.h" #include "shurikens/data/data.h" #include "shurikens/logger.h" #include "shurikens/solver/breadth_search_solver.h" @@ -43,9 +44,10 @@ using std::chrono::duration_cast; using std::chrono::microseconds; using std::chrono::steady_clock; -inline bool solveCPic(); +inline bool solveCPic(bool fullRun); +inline bool solveHangman(); inline bool solveShurikens(bool fullRun); -inline bool solveSudoku(); +inline bool solveSudoku(bool fullRun); int main(int argc, char *argv[]) { // TODO Maybe creating a parsing unit? @@ -53,12 +55,13 @@ int main(int argc, char *argv[]) { auto fullRun = arg == "full"; auto start = steady_clock::now(); - if (!solveCPic() || !solveShurikens(fullRun) || !solveSudoku()) return 1; + if (!solveCPic(fullRun) || !solveHangman() || !solveShurikens(fullRun) || !solveSudoku(fullRun)) return 1; auto end = steady_clock::now(); cout << "All good, we took roughly " << duration_cast(end - start).count() << " microseconds!\n"; } -bool solveCPic() { +bool solveCPic(bool fullRun) { + if (!fullRun) return true; CPic::BruteForceBoardSolver bruteSolver; CPic::HeuristicBoardSolver heuristicSolver; CPic::BoardLogger logger; @@ -100,7 +103,17 @@ bool solveCPic() { return true; } +bool solveHangman() { + Hangman::WordRepository wordRepository; + auto words = wordRepository.words(); + std::cout << "Hangman: We've got " << words.size() << " words\n"; + + return true; +} + bool solveShurikens(bool fullRun) { + if (!fullRun) return true; + Shurikens::BreadthSearchSolver breadthSearchSolver; Shurikens::DepthSearchSolver depthSearchSolver; @@ -140,7 +153,9 @@ bool solveShurikens(bool fullRun) { return true; } -bool solveSudoku() { +bool solveSudoku(bool fullRun) { + if (!fullRun) return true; + Sudoku::BruteForceSolver bruteSolver; Sudoku::HeuristicBoardSolver heuristicSolver; Sudoku::BoardLogger logger; From cf6333b69e20f5bf24beeefc2667f1d1165fe06c Mon Sep 17 00:00:00 2001 From: Emanuel Machado da Silva Date: Fri, 10 Jul 2020 18:17:33 -0300 Subject: [PATCH 15/16] Add the Hangman game, with 2 basic players --- CMakeLists.txt | 3 ++ src/hangman/game.cpp | 59 +++++++++++++++++++++++ src/hangman/game.h | 43 +++++++++++++++++ src/hangman/player/alpha_order_player.cpp | 45 +++++++++++++++++ src/hangman/player/alpha_order_player.h | 44 +++++++++++++++++ src/hangman/player/player.h | 45 +++++++++++++++++ src/hangman/player/random_player.cpp | 46 ++++++++++++++++++ src/hangman/player/random_player.h | 48 ++++++++++++++++++ src/main.cpp | 18 +++++-- 9 files changed, 347 insertions(+), 4 deletions(-) create mode 100644 src/hangman/game.cpp create mode 100644 src/hangman/game.h create mode 100644 src/hangman/player/alpha_order_player.cpp create mode 100644 src/hangman/player/alpha_order_player.h create mode 100644 src/hangman/player/player.h create mode 100644 src/hangman/player/random_player.cpp create mode 100644 src/hangman/player/random_player.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 033b83b8..f620c5bd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,6 +46,9 @@ add_library(puzzles_lib OBJECT src/cpic/solver/brute_force_board_solver.cpp src/cpic/solver/heuristic_board_solver.cpp src/cpic/view/board_logger.cpp + src/hangman/game.cpp + src/hangman/player/alpha_order_player.cpp + src/hangman/player/random_player.cpp src/hangman/word_repository.cpp src/shurikens/data/easy.cpp src/shurikens/data/real.cpp diff --git a/src/hangman/game.cpp b/src/hangman/game.cpp new file mode 100644 index 00000000..7b7b6d90 --- /dev/null +++ b/src/hangman/game.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2020 Emanuel Machado da Silva + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "game.h" + +#include + +using namespace Hangman; + +inline uint8_t play(PlayerSession *player, std::string word) { + auto mistakes = 0; + + while (word.empty() == false) { + auto letter = player->guess(); + + if (std::find(word.begin(), word.end(), letter) == word.end()) { + mistakes++; + } else { + word.erase(std::remove(word.begin(), word.end(), letter), word.end()); + } + } + + return mistakes; +} + +GameResults Game::play(Player *player) { + uint64_t sum = 0; + + std::vector words; + auto completeWords = wordRepository.words(); + for (auto i = 0; i < 10; ++i) { + words.push_back(completeWords[i]); + } + + for (const auto &word : words) { + sum += ::play(player->session(), word); + } + + return {sum / words.size()}; +} diff --git a/src/hangman/game.h b/src/hangman/game.h new file mode 100644 index 00000000..60396558 --- /dev/null +++ b/src/hangman/game.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2020 Emanuel Machado da Silva + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +#include "player/player.h" +#include "word_repository.h" + +#include + +namespace Hangman { + +struct GameResults { + u_int64_t averageMistakes; +}; + +struct Game { + GameResults play(Player *); + +private: + WordRepository wordRepository; +}; + +} diff --git a/src/hangman/player/alpha_order_player.cpp b/src/hangman/player/alpha_order_player.cpp new file mode 100644 index 00000000..08034cb7 --- /dev/null +++ b/src/hangman/player/alpha_order_player.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2020 Emanuel Machado da Silva + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "alpha_order_player.h" + +#include + +using namespace Hangman; + +char AlphaOrderPlayerSession::guess() { + auto letters = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'}; + for (auto letter : letters) { + if (std::find(guesses.begin(), guesses.end(), letter) == guesses.end()) { + guesses.push_back(letter); + return letter; + } + } + + throw 1; +} + +PlayerSession *AlphaOrderPlayer::session() { + currentSession = AlphaOrderPlayerSession(); + return ¤tSession; +} diff --git a/src/hangman/player/alpha_order_player.h b/src/hangman/player/alpha_order_player.h new file mode 100644 index 00000000..bec43104 --- /dev/null +++ b/src/hangman/player/alpha_order_player.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2020 Emanuel Machado da Silva + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +#include "player.h" + +namespace Hangman { + +struct AlphaOrderPlayerSession : public PlayerSession { + std::vector guesses; + + char guess() final; +}; + +struct AlphaOrderPlayer : public Player { + AlphaOrderPlayer() : Player("Alpha") {} + + [[nodiscard]] PlayerSession *session() final; + +private: + AlphaOrderPlayerSession currentSession; +}; + +} diff --git a/src/hangman/player/player.h b/src/hangman/player/player.h new file mode 100644 index 00000000..8495f446 --- /dev/null +++ b/src/hangman/player/player.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2020 Emanuel Machado da Silva + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +#include +#include + +namespace Hangman { + +// TODO: Maybe this should be Player and the other PlayerFactory? Food for thought. +struct PlayerSession { + virtual ~PlayerSession() = default; + + virtual char guess() = 0; +}; + +struct Player { + Player(std::string name) : name(name) {}; + + const std::string name; + + [[nodiscard]] virtual PlayerSession *session() = 0; +}; + +} diff --git a/src/hangman/player/random_player.cpp b/src/hangman/player/random_player.cpp new file mode 100644 index 00000000..ae46f87c --- /dev/null +++ b/src/hangman/player/random_player.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2020 Emanuel Machado da Silva + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "random_player.h" + +#include + +using namespace Hangman; + +RandomPlayerSession::RandomPlayerSession() + : guesses({'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'}) {} + +char RandomPlayerSession::guess() { + assert(guesses.empty() == false); + + auto index = rand() % guesses.size(); + auto letter = guesses[index]; + guesses.erase(guesses.begin() + index); + + return letter; +} + +PlayerSession *RandomPlayer::session() { + currentSession = RandomPlayerSession(); + return ¤tSession; +} diff --git a/src/hangman/player/random_player.h b/src/hangman/player/random_player.h new file mode 100644 index 00000000..270340f9 --- /dev/null +++ b/src/hangman/player/random_player.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2020 Emanuel Machado da Silva + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +#include "player.h" + +#include + +namespace Hangman { + +struct RandomPlayerSession : public PlayerSession { + RandomPlayerSession(); + + std::deque guesses; + + char guess() final; +}; + +struct RandomPlayer : public Player { + RandomPlayer() : Player("Random") {} + + [[nodiscard]] PlayerSession *session() final; + +private: + RandomPlayerSession currentSession; +}; + +} diff --git a/src/main.cpp b/src/main.cpp index 2188ef5f..e7343b6a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -24,7 +24,9 @@ #include "cpic/solver/brute_force_board_solver.h" #include "cpic/solver/heuristic_board_solver.h" #include "cpic/view/board_logger.h" -#include "hangman/word_repository.h" +#include "hangman/game.h" +#include "hangman/player/alpha_order_player.h" +#include "hangman/player/random_player.h" #include "shurikens/data/data.h" #include "shurikens/logger.h" #include "shurikens/solver/breadth_search_solver.h" @@ -104,9 +106,17 @@ bool solveCPic(bool fullRun) { } bool solveHangman() { - Hangman::WordRepository wordRepository; - auto words = wordRepository.words(); - std::cout << "Hangman: We've got " << words.size() << " words\n"; + + Hangman::AlphaOrderPlayer alphaOrderPlayer; + Hangman::RandomPlayer randomPlayer; + + Hangman::Player *players[] = {&alphaOrderPlayer, &randomPlayer}; + Hangman::Game game; + + for (auto player: players) { + auto alphaOrderResults = game.play(player); + std::cout << "Hangman: " << player->name << " had " << alphaOrderResults.averageMistakes << " mistakes on average\n"; + } return true; } From 5d842a1c59e41d1f52409616957f985a07fb5047 Mon Sep 17 00:00:00 2001 From: Emanuel Machado da Silva Date: Tue, 21 Jul 2020 19:27:04 -0300 Subject: [PATCH 16/16] Add a FrequencyAware Hangman player --- CMakeLists.txt | 1 + src/common/collections.h | 43 +++++++++ src/hangman/player/frequency_aware_player.cpp | 89 +++++++++++++++++++ src/hangman/player/frequency_aware_player.h | 52 +++++++++++ src/main.cpp | 5 +- 5 files changed, 188 insertions(+), 2 deletions(-) create mode 100644 src/common/collections.h create mode 100644 src/hangman/player/frequency_aware_player.cpp create mode 100644 src/hangman/player/frequency_aware_player.h diff --git a/CMakeLists.txt b/CMakeLists.txt index f620c5bd..0a7f541e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,6 +48,7 @@ add_library(puzzles_lib OBJECT src/cpic/view/board_logger.cpp src/hangman/game.cpp src/hangman/player/alpha_order_player.cpp + src/hangman/player/frequency_aware_player.cpp src/hangman/player/random_player.cpp src/hangman/word_repository.cpp src/shurikens/data/easy.cpp diff --git a/src/common/collections.h b/src/common/collections.h new file mode 100644 index 00000000..4bc422f1 --- /dev/null +++ b/src/common/collections.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2020 Emanuel Machado da Silva + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +#include + +namespace Puzzles { + +template +struct queue_inserter { + explicit queue_inserter(std::queue &queue) : queue(queue) {} + + queue_inserter operator++() { return *this; } + queue_inserter operator*() { return *this; } + queue_inserter &operator=(const char value) { + queue.push(value); + return *this; + } + +private: + std::queue &queue; +}; +} diff --git a/src/hangman/player/frequency_aware_player.cpp b/src/hangman/player/frequency_aware_player.cpp new file mode 100644 index 00000000..7d26e0f2 --- /dev/null +++ b/src/hangman/player/frequency_aware_player.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2020 Emanuel Machado da Silva + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "frequency_aware_player.h" + +#include "common/collections.h" +#include "hangman/word_repository.h" + +#include +#include + +using namespace Hangman; +using namespace Puzzles; + +typedef uint32_t Frequency; +typedef std::map FrequencyMap; +typedef std::pair FrequencyPair; +typedef std::vector FrequencyVector; + +char FrequencyAwarePlayerSession::guess() { + assert(guesses.empty() == false); + + auto top = guesses.front(); + guesses.pop(); + return top; +} + +PlayerSession *FrequencyAwarePlayer::session() { + if (guessOrder.empty()) init(); + + currentSession = FrequencyAwarePlayerSession(guessOrder); + return ¤tSession; +} + +static inline FrequencyMap createFrequencyMap() { + WordRepository repository; + FrequencyMap frequency; + + for (auto letter = 'A'; letter <= 'Z'; letter++) { + frequency[letter] = 0; + } + + for (const auto &word : repository.words()) { + for (auto letter : word) { + assert(letter >= 'A' && letter <= 'Z'); + frequency[letter]++; + } + } + + return frequency; +} + +inline static FrequencyVector convertToPairs(FrequencyMap frequency) { + FrequencyVector result; + + std::transform(frequency.begin(), frequency.end(), std::back_inserter(result), + [](std::pair pair) { return std::pair(pair.first, pair.second); }); + + std::sort(result.begin(), result.end(), + [](FrequencyPair first, FrequencyPair second) { return first.second > second.second; }); + + return result; +} + +void FrequencyAwarePlayer::init() { + auto frequency = createFrequencyMap(); + auto pairs = convertToPairs(frequency); + + std::transform(pairs.begin(), pairs.end(), queue_inserter(guessOrder), [](auto value) { return value.first; }); +} diff --git a/src/hangman/player/frequency_aware_player.h b/src/hangman/player/frequency_aware_player.h new file mode 100644 index 00000000..88fa73ee --- /dev/null +++ b/src/hangman/player/frequency_aware_player.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2020 Emanuel Machado da Silva + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +#include "player.h" + +#include +#include + +namespace Hangman { + +struct FrequencyAwarePlayerSession : public PlayerSession { + FrequencyAwarePlayerSession(std::queue guesses) : guesses(guesses) {} + char guess() final; + +private: + std::queue guesses; +}; + +struct FrequencyAwarePlayer : public Player { + FrequencyAwarePlayer() : Player("FrequencyAware"), currentSession({}) {} + + [[nodiscard]] PlayerSession *session() final; + +private: + std::queue guessOrder; + FrequencyAwarePlayerSession currentSession; + + void init(); +}; + +} diff --git a/src/main.cpp b/src/main.cpp index e7343b6a..eacbc7bd 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -26,6 +26,7 @@ #include "cpic/view/board_logger.h" #include "hangman/game.h" #include "hangman/player/alpha_order_player.h" +#include "hangman/player/frequency_aware_player.h" #include "hangman/player/random_player.h" #include "shurikens/data/data.h" #include "shurikens/logger.h" @@ -106,11 +107,11 @@ bool solveCPic(bool fullRun) { } bool solveHangman() { - Hangman::AlphaOrderPlayer alphaOrderPlayer; + Hangman::FrequencyAwarePlayer frequencyAwarePlayer; Hangman::RandomPlayer randomPlayer; - Hangman::Player *players[] = {&alphaOrderPlayer, &randomPlayer}; + Hangman::Player *players[] = {&alphaOrderPlayer, &frequencyAwarePlayer, &randomPlayer}; Hangman::Game game; for (auto player: players) {