diff --git a/.gitignore b/.gitignore index c7cd8d9..4dfc335 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,119 @@ *.d fc-sui test-bin +Findfmt.cmake +qrc_resources.cpp +*.zip +html +latex +qrc_resources +*.vtxc +.DS_Store +.vscode +CMakeFiles +conan* +graph_info* +cmake_install.cmake +CMakeCache* +xout/ +build + +# Prerequisites +*.d + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +[Xx]64/ +[Xx]86/ +[Bb]uild/ +bld/ +[Bb]in/ +[Oo]bj/ + +# Visual Studio 2015 cache/options directory +.vs/ +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# Object files +*.o +*.ko +*.elf + + +# Linker output +*.ilk +*.map +*.exp + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tlog +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Libraries +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + +#settings +CMakeSettings.json diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..fe4cfd2 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required (VERSION 3.16) + +project(freecell CXX) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED TRUE) + +file(GLOB_RECURSE vsrc "*.cc") +file(GLOB_RECURSE vhead "*.h") + +list(FILTER vsrc EXCLUDE REGEX "test") +list(FILTER vsrc EXCLUDE REGEX "fc-sui") + +add_library ("${PROJECT_NAME}lib" "${vsrc}" "${vhead}") + +add_executable ("${PROJECT_NAME}" "fc-sui.cc" "${vhead}") +target_link_libraries("${PROJECT_NAME}" PRIVATE "${PROJECT_NAME}lib") # mymod uses symbols from myexe + +add_executable("${PROJECT_NAME}_test" "test.cc" "test-main.cc") +target_link_libraries("${PROJECT_NAME}_test" PRIVATE "${PROJECT_NAME}lib") # mymod uses symbols from myexe + diff --git a/game.cc b/game.cc index 19ecc32..ecc20ae 100644 --- a/game.cc +++ b/game.cc @@ -8,107 +8,107 @@ #include template -std::vector collect_location_pointers(In begin, In end) { - std::vector adresses; - for (auto it=begin; it != end; ++it) - adresses.push_back(it); +std::vector collect_location_pointers(In begin, In end) { + std::vector adresses; + for (auto it = begin; it != end; ++it) + adresses.push_back(std::addressof(*it)); - return adresses; + return adresses; } GameState::GameState(void) { - recalculatePointerArrays_(); + recalculatePointerArrays_(); } void GameState::recalculatePointerArrays_(void) { - assert(nb_freecells == nb_homes); - for (int i=0; i topCards(const GameState &gs) { - std::vector cards; - - for (auto &cs : gs.non_homes) { - auto opt_card = cs->topCard(); - if (opt_card.has_value()) - cards.push_back(*opt_card); - } - - return cards; +bool operator==(const GameState& lhs, const GameState& rhs) { + auto lhs_tuple = std::tie(lhs.homes, lhs.stacks, lhs.free_cells); + auto rhs_tuple = std::tie(rhs.homes, rhs.stacks, rhs.free_cells); + return lhs_tuple == rhs_tuple; } -int moveCardsFromHomes(GameState *gs, int max_nb_cards, size_t stack_begin, size_t stack_end, std::default_random_engine rng) { - int nb_cards_moved = 0; - for (; nb_cards_moved < max_nb_cards; ++nb_cards_moved) { - std::vector considered_froms{&gs->homes[nb_cards_moved % gs->homes.size()]}; - std::vector considered_tos = collect_location_pointers(gs->stacks.begin() + stack_begin, gs->stacks.begin() + stack_end); +std::vector topCards(const GameState& gs) { + std::vector cards; - auto moves = availableMoves( - considered_froms.begin(), - considered_froms.end(), - considered_tos.begin(), - considered_tos.end() - ); + for (auto& cs : gs.non_homes) { + auto opt_card = cs->topCard(); + if (opt_card.has_value()) + cards.push_back(*opt_card); + } - if (moves.size() == 0) - break; + return cards; +} - int pick = std::uniform_int_distribution(0, moves.size()-1)(rng); - // we have a non-const access to GameState (gs), so we are allowed to de-const the pointers in RawMove - move(const_cast(moves[pick].first), const_cast(moves[pick].second)); - } +int moveCardsFromHomes(GameState* gs, int max_nb_cards, size_t stack_begin, size_t stack_end, std::default_random_engine rng) { + int nb_cards_moved = 0; + for (; nb_cards_moved < max_nb_cards; ++nb_cards_moved) { + std::vector considered_froms{ &gs->homes[nb_cards_moved % gs->homes.size()] }; + std::vector considered_tos = collect_location_pointers(gs->stacks.begin() + stack_begin, gs->stacks.begin() + stack_end); + + auto moves = availableMoves( + considered_froms.begin(), + considered_froms.end(), + considered_tos.begin(), + considered_tos.end() + ); + + if (moves.size() == 0) + break; + + int pick = std::uniform_int_distribution(0, moves.size() - 1)(rng); + // we have a non-const access to GameState (gs), so we are allowed to de-const the pointers in RawMove + move(const_cast(moves[pick].first), const_cast(moves[pick].second)); + } - return nb_cards_moved; + return nb_cards_moved; } -void initializeFullRandom(GameState *gs, std::default_random_engine &rng) { +void initializeFullRandom(GameState* gs, std::default_random_engine& rng) { std::vector all_cards; - for (auto color: colors_list) { + for (auto color : colors_list) { for (int value = 1; value <= king_value; ++value) - all_cards.push_back({color, value}); + all_cards.push_back({ color, value }); } std::vector order; @@ -129,221 +129,221 @@ void initializeFullRandom(GameState *gs, std::default_random_engine &rng) { assert(card_id_it == order.end()); } -void forceMove(CardStorage *from, WorkStack *to) { - to->forceCard(*from->getCard()); +void forceMove(CardStorage* from, WorkStack* to) { + to->forceCard(*from->getCard()); } -const CardStorage * ptrFromLoc(const GameState &gs, Location const& loc) { - switch (loc.cl) { - case LocationClass::FreeCells: - return &gs.free_cells[loc.id]; - break; - case LocationClass::Homes: - return &gs.homes[loc.id]; - break; - case LocationClass::Stacks: - return &gs.stacks[loc.id]; - break; - default: - return nullptr; - } +const CardStorage* ptrFromLoc(const GameState& gs, Location const& loc) { + switch (loc.cl) { + case LocationClass::FreeCells: + return &gs.free_cells[loc.id]; + break; + case LocationClass::Homes: + return &gs.homes[loc.id]; + break; + case LocationClass::Stacks: + return &gs.stacks[loc.id]; + break; + default: + return nullptr; + } } -bool operator== (const Location &lhs, const Location &rhs) { - return lhs.cl == rhs.cl && lhs.id == rhs.id; +bool operator== (const Location& lhs, const Location& rhs) { + return lhs.cl == rhs.cl && lhs.id == rhs.id; } -bool operator!= (const Location &lhs, const Location &rhs) { - return !(lhs == rhs); +bool operator!= (const Location& lhs, const Location& rhs) { + return !(lhs == rhs); } // to be used only with continuous-storage containers template -bool isInContainer(const CardStorage *ptr, const T& container) { - return ptr >= &container[0] && ptr <= &container[container.size()-1]; +bool isInContainer(const CardStorage* ptr, const T& container) { + return ptr >= &container[0] && ptr <= &container[container.size() - 1]; } // to be used only with continuous-storage containers template -long positionInContainer(const CardStorage *ptr, const T& container) { - return static_cast(ptr) - &container[0]; +long positionInContainer(const CardStorage* ptr, const T& container) { + return static_cast(ptr) - &container[0]; } -Location locFromPtr(const GameState &gs, const CardStorage *ptr) { - if (isInContainer(ptr, gs.homes)) - return {LocationClass::Homes, positionInContainer(ptr, gs.homes)}; - else if (isInContainer(ptr, gs.stacks)) - return {LocationClass::Stacks, positionInContainer(ptr, gs.stacks)}; - else if (isInContainer(ptr, gs.free_cells)) - return {LocationClass::FreeCells, positionInContainer(ptr, gs.free_cells)}; - else - throw std::out_of_range("Pointer doesn't match any of homes, freecells or stacks in the given GameState"); +Location locFromPtr(const GameState& gs, const CardStorage* ptr) { + if (isInContainer(ptr, gs.homes)) + return { LocationClass::Homes, positionInContainer(ptr, gs.homes) }; + else if (isInContainer(ptr, gs.stacks)) + return { LocationClass::Stacks, positionInContainer(ptr, gs.stacks) }; + else if (isInContainer(ptr, gs.free_cells)) + return { LocationClass::FreeCells, positionInContainer(ptr, gs.free_cells) }; + else + throw std::out_of_range("Pointer doesn't match any of homes, freecells or stacks in the given GameState"); } -void initializeGameState(GameState *gs, std::default_random_engine &rng) { - for (size_t i=0; ihomes[i].acceptCard({colors_list[i], j}); - } +void initializeGameState(GameState* gs, std::default_random_engine& rng) { + for (size_t i = 0; i < colors_list.size(); ++i) { + for (int j = 1; j <= king_value; ++j) + gs->homes[i].acceptCard({ colors_list[i], j }); + } - moveCardsFromHomes(gs, 20, 0, 5, rng); - moveCardsFromHomes(gs, 32, 0, 8, rng); + moveCardsFromHomes(gs, 20, 0, 5, rng); + moveCardsFromHomes(gs, 32, 0, 8, rng); } -std::optional> findIrreversibleMove(GameState *gs, std::default_random_engine &rng) { - // number of cards that cannot be moved from the given stack - // as it is irreversibely placed in its location - std::vector frozen_level(gs->stacks.size(), 0); - - std::vector possible_from; - for (auto &fc : gs->free_cells) { - if (fc.topCard().has_value()) - possible_from.push_back(&fc); - } - for (size_t i=0; i < gs->stacks.size(); ++i) { - auto &stack = gs->stacks[i]; - if (stack.topCard().has_value() && stack.nbCards() > frozen_level[i]) - possible_from.push_back(&stack); - } - if (possible_from.size() == 0) - return std::nullopt; - - int pick_from = std::uniform_int_distribution(0, possible_from.size()-1)(rng); - auto from = possible_from[pick_from]; - - std::vector possible_to; - for (size_t i=0; i < gs->stacks.size(); ++i) { - auto &stack = gs->stacks[i]; - bool move_elsewhere = &stack != from; - bool stack_not_overfull =stack.nbCards() <= 7; - if (move_elsewhere && stack_not_overfull) - possible_to.push_back(&stack); - } - if (possible_to.size() == 0) - return std::nullopt; - - int pick_to = std::uniform_int_distribution(0, possible_to.size()-1)(rng); - auto to = possible_to[pick_to]; - - return std::make_pair(from, to); +std::optional> findIrreversibleMove(GameState* gs, std::default_random_engine& rng) { + // number of cards that cannot be moved from the given stack + // as it is irreversibely placed in its location + std::vector frozen_level(gs->stacks.size(), 0); + + std::vector possible_from; + for (auto& fc : gs->free_cells) { + if (fc.topCard().has_value()) + possible_from.push_back(&fc); + } + for (size_t i = 0; i < gs->stacks.size(); ++i) { + auto& stack = gs->stacks[i]; + if (stack.topCard().has_value() && stack.nbCards() > frozen_level[i]) + possible_from.push_back(&stack); + } + if (possible_from.size() == 0) + return std::nullopt; + + int pick_from = std::uniform_int_distribution(0, possible_from.size() - 1)(rng); + auto from = possible_from[pick_from]; + + std::vector possible_to; + for (size_t i = 0; i < gs->stacks.size(); ++i) { + auto& stack = gs->stacks[i]; + bool move_elsewhere = &stack != from; + bool stack_not_overfull = stack.nbCards() <= 7; + if (move_elsewhere && stack_not_overfull) + possible_to.push_back(&stack); + } + if (possible_to.size() == 0) + return std::nullopt; + + int pick_to = std::uniform_int_distribution(0, possible_to.size() - 1)(rng); + auto to = possible_to[pick_to]; + + return std::make_pair(from, to); } -auto findHomeFor(const GameState &gs, Card card) -> decltype(gs.homes)::const_iterator { - return std::find_if( - gs.homes.begin(), - gs.homes.end(), - [&](const CardStorage &cs){return cs.canAccept(card);} - ); +auto findHomeFor(const GameState& gs, Card card) -> decltype(gs.homes)::const_iterator{ + return std::find_if( + gs.homes.begin(), + gs.homes.end(), + [&](const CardStorage& cs) {return cs.canAccept(card); } + ); } -bool cardIsHome(const GameState &gs, Card card) { - for (const auto & home : gs.homes) { - auto opt_top_card = home.topCard(); - if (!opt_top_card.has_value()) - continue; +bool cardIsHome(const GameState& gs, Card card) { + for (const auto& home : gs.homes) { + auto opt_top_card = home.topCard(); + if (!opt_top_card.has_value()) + continue; - if (opt_top_card->color == card.color && opt_top_card->value >= card.value) - return true; - } + if (opt_top_card->color == card.color && opt_top_card->value >= card.value) + return true; + } - return false; + return false; } -bool cardCouldGoHome(const GameState &gs, Card card) { - // Aces can always go home - // Thus, twos can go too, as an Ace will never need to rest - // on a two - if (card.value == 1 or card.value == 2) - return true; +bool cardCouldGoHome(const GameState& gs, Card card) { + // Aces can always go home + // Thus, twos can go too, as an Ace will never need to rest + // on a two + if (card.value == 1 || card.value == 2) + return true; - auto render_color{render_color_map.at(card.color)}; - std::vector opposite_rc_colors; - bool safe = true; + auto render_color{ render_color_map.at(card.color) }; + std::vector opposite_rc_colors; + bool safe = true; - for (auto & color : colors_list) { - if (render_color_map.at(color) == render_color) - continue; + for (auto& color : colors_list) { + if (render_color_map.at(color) == render_color) + continue; - if (!cardIsHome(gs, {color, card.value-1})) - safe = false; - } + if (!cardIsHome(gs, { color, card.value - 1 })) + safe = false; + } - return safe; + return safe; } -std::vector safeHomeMoves(const GameState &gs) { - std::vector moves; +std::vector safeHomeMoves(const GameState& gs) { + std::vector moves; - for (auto &cs : gs.non_homes) { - auto opt_card = cs->topCard(); + for (auto& cs : gs.non_homes) { + auto opt_card = cs->topCard(); - if (!opt_card.has_value()) - continue; + if (!opt_card.has_value()) + continue; - auto home_it = findHomeFor(gs, *opt_card); - if (home_it != gs.homes.end() && cardCouldGoHome(gs, *opt_card)) - moves.push_back({cs, home_it}); - } + auto home_it = findHomeFor(gs, *opt_card); + if (home_it != gs.homes.end() && cardCouldGoHome(gs, *opt_card)) + moves.push_back({ cs, std::addressof(*home_it) }); + } - return moves; + return moves; } -std::ostream& operator<< (std::ostream& os, const GameState & state) { - os << "Homes: " << - state.homes[0] << " " << - state.homes[1] << " " << - state.homes[2] << " " << - state.homes[3] << "\n"; - - os << "FreeCells: " << - state.free_cells[0] << " " << - state.free_cells[1] << " " << - state.free_cells[2] << " " << - state.free_cells[3] << "\n"; - - for (auto stack : state.stacks) { - os << stack << "\n"; - } +std::ostream& operator<< (std::ostream& os, const GameState& state) { + os << "Homes: " << + state.homes[0] << " " << + state.homes[1] << " " << + state.homes[2] << " " << + state.homes[3] << "\n"; + + os << "FreeCells: " << + state.free_cells[0] << " " << + state.free_cells[1] << " " << + state.free_cells[2] << " " << + state.free_cells[3] << "\n"; + + for (auto stack : state.stacks) { + os << stack << "\n"; + } - return os; + return os; } -std::ostream& operator<< (std::ostream& os, const Location & loc) { - switch (loc.cl) { - case LocationClass::Homes: - os << "Home "; - break; - case LocationClass::Stacks: - os << "Stack "; - break; - case LocationClass::FreeCells: - os << "FreeCell "; - break; - } - os << loc.id; - - return os; +std::ostream& operator<< (std::ostream& os, const Location& loc) { + switch (loc.cl) { + case LocationClass::Homes: + os << "Home "; + break; + case LocationClass::Stacks: + os << "Stack "; + break; + case LocationClass::FreeCells: + os << "FreeCell "; + break; + } + os << loc.id; + + return os; } GameState EasyProducer::produce() { - GameState gs; - - initializeGameState(&gs, rng_); - for (int i = 0; i < difficulty_; ++i) { - auto move = findIrreversibleMove(&gs, rng_); - if (!move.has_value()) - break; - forceMove(move->first, move->second); - } + GameState gs; + + initializeGameState(&gs, rng_); + for (int i = 0; i < difficulty_; ++i) { + auto move = findIrreversibleMove(&gs, rng_); + if (!move.has_value()) + break; + forceMove(move->first, move->second); + } - return gs; + return gs; } GameState RandomProducer::produce() { - GameState gs; - initializeFullRandom(&gs, rng_); + GameState gs; + initializeFullRandom(&gs, rng_); - return gs; + return gs; } diff --git a/sui-solution.cc b/sui-solution.cc index 6dd94f0..d19dcd0 100644 --- a/sui-solution.cc +++ b/sui-solution.cc @@ -1,17 +1,18 @@ #include "search-strategies.h" +#include -std::vector BreadthFirstSearch::solve(const SearchState &init_state) { +std::vector BreadthFirstSearch::solve(const SearchState& init_state) { return {}; } -std::vector DepthFirstSearch::solve(const SearchState &init_state) { +std::vector DepthFirstSearch::solve(const SearchState& init_state) { return {}; } -double StudentHeuristic::distanceLowerBound(const GameState &state) const { - return 0; +double StudentHeuristic::distanceLowerBound(const GameState& state) const { + return 0; } -std::vector AStarSearch::solve(const SearchState &init_state) { +std::vector AStarSearch::solve(const SearchState& init_state) { return {}; }