{"id":4497,"date":"2024-10-21T10:14:29","date_gmt":"2024-10-21T10:14:29","guid":{"rendered":"https:\/\/algocademy.com\/blog\/common-logic-puzzles-and-how-to-solve-them-sharpening-your-coding-skills\/"},"modified":"2026-10-15T10:34:49","modified_gmt":"2026-10-15T10:34:49","slug":"common-logic-puzzles-and-how-to-solve-them-sharpening-your-coding-skills","status":"publish","type":"post","link":"https:\/\/algocademy.com\/blog\/common-logic-puzzles-and-how-to-solve-them-sharpening-your-coding-skills\/","title":{"rendered":"Common Logic Puzzles and How to Solve Them in Code"},"content":{"rendered":"<p>Classic logic puzzles are small versions of real algorithm patterns: river crossing is breadth-first search over states, Tower of Hanoi is recursion, N-Queens is backtracking, Monty Hall is probability you can check by simulation, and the Josephus problem is a recurrence with modular arithmetic. Below, each puzzle comes with the idea that solves it and Python code that has been run, followed by a general method for attacking a puzzle you have not seen.<\/p>\n<h2>Are puzzles still asked in interviews?<\/h2>\n<p>Pure brainteasers (&#8220;how many golf balls fit in a bus?&#8221;) have mostly gone out of fashion in software interviews, and several large companies have said publicly that they stopped using them. What remains common are problems that look like puzzles but are algorithm questions in disguise: state search, recursion, backtracking, counting. Learning the puzzles below is useful mainly because they teach those patterns in a form you can reason about on paper.<\/p>\n<h2>1. River crossing: search over states<\/h2>\n<p><strong>The puzzle.<\/strong> A farmer must carry a fox, a chicken and a sack of grain across a river. The boat holds the farmer and one item. Left alone, the fox eats the chicken and the chicken eats the grain.<\/p>\n<p><strong>The idea.<\/strong> Describe the world as a state: which bank each of the farmer, fox, chicken and grain is on. A move flips the farmer and optionally one item that is on the same bank. Some states are illegal (fox and chicken alone together). Breadth-first search from the start state finds the shortest sequence of moves, and keeping a &#8220;previous state&#8221; map lets you read the answer back.<\/p>\n<pre><code class=\"language-python\">from collections import deque\n\n# state = (farmer, fox, chicken, grain); 0 = start bank, 1 = far bank\ndef safe(s):\n    f, fox, ch, gr = s\n    if fox == ch != f: return False     # fox alone with chicken\n    if ch == gr != f: return False      # chicken alone with grain\n    return True\n\ndef river():\n    names = [\"fox\", \"chicken\", \"grain\"]\n    start, goal = (0, 0, 0, 0), (1, 1, 1, 1)\n    prev, q = {start: None}, deque([start])\n    while q:\n        s = q.popleft()\n        if s == goal:\n            break\n        for i in [None, 1, 2, 3]:             # carry nothing, or one item\n            if i is not None and s[i] != s[0]:\n                continue                       # item is on the other bank\n            n = list(s)\n            n[0] ^= 1\n            if i is not None:\n                n[i] ^= 1\n            n = tuple(n)\n            if n not in prev and safe(n):\n                prev[n] = (s, i)\n                q.append(n)\n    steps, s = [], goal\n    while prev[s]:\n        p, i = prev[s]\n        steps.append((\"take \" + names[i - 1]) if i else \"go alone\")\n        s = p\n    return steps[::-1]\n\nprint(river())\n# ['take chicken', 'go alone', 'take fox', 'take chicken', 'take grain', 'go alone', 'take chicken']<\/code><\/pre>\n<p>That is seven crossings, and BFS guarantees none is shorter. (Swapping &#8220;take fox&#8221; and &#8220;take grain&#8221; gives a second valid answer; BFS returns one of them.) The same pattern solves sliding-tile puzzles, word ladders and &#8220;minimum moves&#8221; problems: define the state, generate legal neighbors, avoid revisiting states. Try it in our <a href=\"https:\/\/algocademy.com\/link\/?lang=py&amp;problem=bfs-min-distance-to-every-vertex&amp;solution=1\">BFS min-distance lesson<\/a>.<\/p>\n<h2>2. Tower of Hanoi: recursion<\/h2>\n<p><strong>The puzzle.<\/strong> Move a stack of n disks from one rod to another, one disk at a time, never placing a larger disk on a smaller one, using a third rod as a helper.<\/p>\n<p><strong>The idea.<\/strong> To move n disks, move the top n-1 disks out of the way onto the helper rod, move the largest disk, then move the n-1 disks on top of it. The base case is zero disks: do nothing. The number of moves is 2<sup>n<\/sup> &#8211; 1, so the running time is exponential and unavoidable; no algorithm can do fewer moves.<\/p>\n<pre><code class=\"language-python\">def hanoi(n, source, target, helper, moves):\n    if n == 0:\n        return\n    hanoi(n - 1, source, helper, target, moves)\n    moves.append((source, target))\n    hanoi(n - 1, helper, target, source, moves)\n\nmoves = []\nhanoi(3, \"A\", \"C\", \"B\", moves)\nprint(len(moves), moves)\n# 7 [('A', 'C'), ('A', 'B'), ('C', 'B'), ('A', 'C'), ('B', 'A'), ('B', 'C'), ('A', 'C')]<\/code><\/pre>\n<p>The lesson is how to trust recursion: assume the function already works for n-1, and only describe how n relies on it. Practice that skill with the <a href=\"https:\/\/algocademy.com\/link\/?lang=py&amp;problem=recursion&amp;solution=1\">recursion lesson<\/a>.<\/p>\n<h2>3. N-Queens: backtracking<\/h2>\n<p><strong>The puzzle.<\/strong> Place n queens on an n-by-n board so that none attack another. The eight-queens case has 92 solutions.<\/p>\n<p><strong>The idea.<\/strong> Place one queen per row. For each row, try each column that is not attacked, recurse to the next row, and undo the placement if it leads nowhere. Attacks are cheap to check with three sets: used columns, used &#8220;\\&#8221; diagonals (row &#8211; col is constant) and used &#8220;\/&#8221; diagonals (row + col is constant).<\/p>\n<pre><code class=\"language-python\">def queens(n):\n    cols, d1, d2 = set(), set(), set()\n\n    def place(row):\n        if row == n:\n            return 1\n        total = 0\n        for c in range(n):\n            if c in cols or row - c in d1 or row + c in d2:\n                continue\n            cols.add(c); d1.add(row - c); d2.add(row + c)\n            total += place(row + 1)\n            cols.remove(c); d1.remove(row - c); d2.remove(row + c)\n        return total\n\n    return place(0)\n\nprint([queens(n) for n in range(1, 9)])\n# [1, 0, 0, 2, 10, 4, 40, 92]<\/code><\/pre>\n<p>The counts for n = 1 to 8 match the known values (1, 0, 0, 2, 10, 4, 40, 92). Backtracking is the same skeleton for Sudoku, generating permutations and subsets, and parentheses generation. The skeleton is: choose, explore, un-choose. See the <a href=\"https:\/\/algocademy.com\/link\/?lang=py&amp;problem=n-queens&amp;solution=1\">N Queens lesson<\/a>.<\/p>\n<h2>4. Monty Hall: check your intuition with a simulation<\/h2>\n<p><strong>The puzzle.<\/strong> Behind one of three doors is a car, behind the others goats. You pick a door. The host, who knows where the car is, opens a different door showing a goat and offers you a switch. Should you switch?<\/p>\n<p><strong>The answer.<\/strong> Yes. Your first pick is right with probability 1\/3. If it was wrong (probability 2\/3), the host&#8217;s forced choice leaves the car behind the only other closed door, so switching wins 2\/3 of the time. When intuition and math disagree, simulate:<\/p>\n<pre><code class=\"language-python\">import random\n\ndef monty(switch, trials=100000, seed=1):\n    rnd = random.Random(seed)\n    wins = 0\n    for _ in range(trials):\n        car, pick = rnd.randrange(3), rnd.randrange(3)\n        opened = next(d for d in range(3) if d != pick and d != car)\n        if switch:\n            pick = next(d for d in range(3) if d != pick and d != opened)\n        wins += pick == car\n    return wins \/ trials\n\nprint(monty(False), monty(True))\n# 0.3328 0.6672<\/code><\/pre>\n<p>The simulation prints about 1\/3 for staying and 2\/3 for switching (the exact numbers depend on the random seed). The important assumption is that the host always opens a goat door on purpose. If he opened a random door and happened to show a goat, switching would give no advantage, which is a good follow-up to mention.<\/p>\n<h2>5. The Josephus problem: find the recurrence<\/h2>\n<p><strong>The puzzle.<\/strong> n people stand in a circle. Starting at person 1, count k people and eliminate the k-th, then continue counting from the next person. Who is the last survivor?<\/p>\n<p><strong>The idea.<\/strong> Simulating with a list works and costs O(n*k) or O(n^2) depending on the removal method. A better view: let J(i) be the survivor&#8217;s 0-based position in a circle of size i. After the first elimination there are i-1 people, and positions shift by k, so J(i) = (J(i-1) + k) mod i, with J(1) = 0. That gives an O(n) loop with O(1) space.<\/p>\n<pre><code class=\"language-python\">def josephus(n, k):\n    pos = 0                          # survivor index for a circle of 1\n    for size in range(2, n + 1):\n        pos = (pos + k) % size\n    return pos + 1                   # convert to 1-based\n\ndef brute(n, k):                     # simulation, to cross-check\n    people, i = list(range(1, n + 1)), 0\n    while len(people) &gt; 1:\n        i = (i + k - 1) % len(people)\n        people.pop(i)\n    return people[0]\n\nprint(josephus(7, 3), josephus(41, 3))\n# 4 31\nprint(all(brute(n, k) == josephus(n, k) for n in range(1, 40) for k in range(1, 8)))\n# True<\/code><\/pre>\n<p>The brute-force version is worth keeping as a test oracle: compare the clever solution against the simple one on small inputs. That habit catches off-by-one errors in interviews and in real code.<\/p>\n<h2>A method for any puzzle<\/h2>\n<ol>\n<li><strong>Restate the rules<\/strong> and check them against a tiny example.<\/li>\n<li><strong>Write down the state.<\/strong> What is the smallest set of facts that fully describes the situation (bank positions, rods, columns used)?<\/li>\n<li><strong>Solve small cases by hand<\/strong> (n = 1, 2, 3) and look for a pattern or a recursion.<\/li>\n<li><strong>Pick the pattern:<\/strong> shortest sequence of moves means BFS; &#8220;all valid arrangements&#8221; means backtracking; &#8220;solve the same problem on a smaller input&#8221; means recursion or dynamic programming; &#8220;which is more likely&#8221; means simulate, then prove.<\/li>\n<li><strong>Write a slow, obviously correct version first<\/strong>, then optimize and compare the two.<\/li>\n<li><strong>State the complexity<\/strong> and the edge cases (n = 0, k = 1, impossible instances).<\/li>\n<\/ol>\n<h2>Where to go next<\/h2>\n<p>If puzzles are your route into interview preparation, move on quickly to the problem types they feed: BFS and DFS on graphs, recursion, backtracking, and dynamic programming. Our post on <a href=\"https:\/\/algocademy.com\/blog\/10-best-coding-exercises-for-mastering-recursion-from-beginner-to-advanced\/\">recursion exercises<\/a> is a good next step, and <a href=\"https:\/\/app.algocademy.com\/\">AlgoCademy<\/a> has guided lessons on each of these topics.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Five classic logic puzzles (river crossing, Tower of Hanoi, N-Queens, Monty Hall, Josephus) with the algorithm behind each and tested Python solutions.<\/p>\n","protected":false},"author":1,"featured_media":4496,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[23],"tags":[],"class_list":["post-4497","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-problem-solving"],"_links":{"self":[{"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/posts\/4497"}],"collection":[{"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/comments?post=4497"}],"version-history":[{"count":1,"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/posts\/4497\/revisions"}],"predecessor-version":[{"id":8723,"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/posts\/4497\/revisions\/8723"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/media\/4496"}],"wp:attachment":[{"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/media?parent=4497"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/categories?post=4497"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/tags?post=4497"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}