Skip to content

Instantly share code, notes, and snippets.

@karagog
Last active October 6, 2022 06:45
Show Gist options
  • Select an option

  • Save karagog/16d3edaf9d86828fab68c21f057d2f0d to your computer and use it in GitHub Desktop.

Select an option

Save karagog/16d3edaf9d86828fab68c21f057d2f0d to your computer and use it in GitHub Desktop.

Revisions

  1. karagog revised this gist Oct 6, 2022. 1 changed file with 3 additions and 4 deletions.
    7 changes: 3 additions & 4 deletions main.cc
    Original file line number Diff line number Diff line change
    @@ -3,10 +3,9 @@
    #include <vector>
    using namespace std;

    // This class solves the Chinese Rings puzzle, optionally printing each
    // move, and tells you how many moves it takes to solve it. It runs in O(2^N)
    // time, because it actually simulates each move and it takes 2^N - 1 moves in
    // the worst case.
    // This class solves the Chinese Rings puzzle (https://en.wikipedia.org/wiki/Baguenaudier),
    // optionally printing each move, and tells you how many moves it takes to solve it. It runs in O(2^N)
    // time, because it actually simulates each move and it takes 2^N - 1 moves in the worst case.
    class ChineseRings {
    public:
    // Initializes the solver with the given state. Pass verbose = false to
  2. karagog revised this gist Oct 6, 2022. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion main.cc
    Original file line number Diff line number Diff line change
    @@ -63,7 +63,7 @@ class ChineseRings {
    // Prints the last move to the console, for visual inspection.
    void PrintLastMove(int N) {
    string note = "Initial Position";
    if (N > 0)
    if (N >= 0)
    note = state_[N] ? "Raise" : "Lower";
    cout << String() << " - " << note << " Index " << N << endl;
    }
  3. karagog revised this gist Oct 6, 2022. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion main.cc
    Original file line number Diff line number Diff line change
    @@ -88,7 +88,7 @@ class ChineseRings {
    int main(int argc, char **argv) {
    // Solve the puzzle when all rings are initially "on".
    vector<bool> input{true, true, true, true, true, true, true, true, true};
    ChineseRings rings(input, /*verbose=*/false);
    ChineseRings rings(input, /*verbose=*/true);
    rings.Solve();
    cout << "# of moves: " << rings.move_count() << endl;
    return 0;
  4. karagog renamed this gist Oct 6, 2022. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  5. karagog created this gist Oct 6, 2022.
    95 changes: 95 additions & 0 deletions gistfile1.txt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,95 @@
    #include <iostream>
    #include <string>
    #include <vector>
    using namespace std;

    // This class solves the Chinese Rings puzzle, optionally printing each
    // move, and tells you how many moves it takes to solve it. It runs in O(2^N)
    // time, because it actually simulates each move and it takes 2^N - 1 moves in
    // the worst case.
    class ChineseRings {
    public:
    // Initializes the solver with the given state. Pass verbose = false to
    // suppress output, in case you're solving a large input.
    ChineseRings(const vector<bool> &initial_state, bool verbose = true)
    : state_(initial_state), verbose_(verbose) {}

    // If unspecified, then we'll solve for all rings off. Otherwise we solve for
    // the desired state.
    void Solve(const vector<bool> &desired_state = {}) {
    if (!desired_state.empty() && desired_state.size() != state_.size())
    cout << "Desired state must be the same size as initial state" << endl;

    PrintLastMove(-1); // show the initial state

    // We can solve each ring in order, starting from the largest index to the
    // smallest, since each ring is only limited by the rings that precede it.
    for (int i = state_.size() - 1; i >= 0; i--)
    SetRing(i, desired_state.empty() ? false : desired_state[i]);
    }

    int move_count() const { return move_cnt_; }

    private:
    // Sets the given ring N to the target state. This mutates all rings <= N, but
    // does not mutate any rings > N. It guarantees that the given index will be
    // set to the target upon return, although there is no guarantee about the
    // rings < N.
    //
    // This method is recursive, with maximum call depth of N.
    void SetRing(int N, bool target) {
    // Look at the current ring state.
    bool cur = state_[N];
    if (cur == target)
    return; // ring is already in desired state

    // We can change the ring at N=0 any time we want. Rings at N > 0 must
    // satisfy a precondition in order to be changed.
    if (N > 0) {
    // In order to change the state of the given ring, the ring at N-1 must be
    // the only one < N which is on. We don't care about any rings > N.
    SetRing(N - 1, true); // put on the ring at N-1
    for (int i = N - 2; i >= 0; i--)
    SetRing(i, false); // take off all the other rings < N
    }

    // Now we are free to change the state of the current ring.
    state_[N] = target;
    move_cnt_++;
    if (verbose_)
    PrintLastMove(N);
    }

    // Prints the last move to the console, for visual inspection.
    void PrintLastMove(int N) {
    string note = "Initial Position";
    if (N > 0)
    note = state_[N] ? "Raise" : "Lower";
    cout << String() << " - " << note << " Index " << N << endl;
    }

    // Serializes the current puzzle state into a string.
    // 'N' indicates the ring is 'on' and 'F' indicates the ring is 'off'.
    string String() const {
    string ret;
    for (bool ring : state_) {
    ret += ring ? "N" : "F";
    }
    return ret;
    }

    // True = ON, False = OFF
    vector<bool> state_;
    vector<int> moves_;
    int64_t move_cnt_ = 0;
    bool verbose_ = false;
    };

    int main(int argc, char **argv) {
    // Solve the puzzle when all rings are initially "on".
    vector<bool> input{true, true, true, true, true, true, true, true, true};
    ChineseRings rings(input, /*verbose=*/false);
    rings.Solve();
    cout << "# of moves: " << rings.move_count() << endl;
    return 0;
    }