👨‍💻
CTFs
HomePlaygroundOSCPBuy Me a Flag 🚩
  • 🚩Zeyu's CTF Writeups
  • Home
  • Playground
  • OSCP
  • My Challenges
    • SEETF 2023
    • The InfoSecurity Challenge 2022
    • SEETF 2022
    • Cyber League Major 1
    • STANDCON CTF 2021
      • Space Station
      • Star Cereal
      • Star Cereal 2
      • Mission Control
      • Rocket Science
      • Space University of Interior Design
      • Rocket Ship Academy
      • Space Noise
  • 2023
    • DEF CON CTF 2023 Qualifiers
    • hxp CTF
      • true_web_assembly
    • HackTM CTF Qualifiers
      • Crocodilu
      • secrets
      • Hades
  • 2022
    • niteCTF 2022
      • Undocumented js-api
      • js-api
    • STACK the Flags 2022
      • Secret of Meow Olympurr
      • The Blacksmith
      • GutHib Actions
      • Electrogrid
      • BeautyCare
    • LakeCTF Qualifiers
      • People
      • Clob-Mate
      • So What? Revenge
    • The InfoSecurity Challenge 2022
      • Level 1 - Slay The Dragon
      • Level 2 - Leaky Matrices
      • Level 3 - PATIENT0
      • Level 4B - CloudyNekos
      • Level 5B - PALINDROME's Secret (Author Writeup)
    • BalsnCTF 2022
      • 2linenodejs
      • Health Check
    • BSidesTLV 2022 CTF
      • Smuggler
      • Wild DevTools
      • Tropical API
    • Grey Cat The Flag 2022
    • DEF CON CTF 2022 Qualifiers
    • Securinets CTF Finals 2022
      • StrUggLe
      • XwaSS ftw?
      • Strong
      • Artist
    • NahamCon CTF 2022
      • Flaskmetal Alchemist
      • Hacker TS
      • Two For One
      • Deafcon
      • OTP Vault
      • Click Me
      • Geezip
      • Ostrich
      • No Space Between Us
    • Securinets CTF Quals 2022
      • Document-Converter
      • PlanetSheet
      • NarutoKeeper
    • CTF.SG CTF
      • Asuna Waffles
      • Senpai
      • We know this all too well
      • Don't Touch My Flag
      • Wildest Dreams Part 2
      • Chopsticks
    • YaCTF 2022
      • Shiba
      • Flag Market
      • Pasteless
      • Secretive
      • MetaPDF
      • Crackme
    • DiceCTF 2022
      • knock-knock
      • blazingfast
    • TetCTF 2022
      • 2X-Service
      • Animals
      • Ezflag Level 1
  • 2021
    • hxp CTF 2021
    • HTX Investigator's Challenge 2021
    • Metasploit Community CTF
    • MetaCTF CyberGames
      • Look, if you had one shot
      • Custom Blog
      • Yummy Vegetables
      • Ransomware Patch
      • I Hate Python
      • Interception
    • CyberSecurityRumble CTF
      • Lukas App
      • Finance Calculat0r 2021
      • Personal Encryptor with Nonbreakable Inforation-theoretic Security
      • Enterprice File Sharing
      • Payback
      • Stonks Street Journal
    • The InfoSecurity Challenge (TISC) 2021
      • Level 4 - The Magician's Den
      • Level 3 - Needle in a Greystack
      • Level 2 - Dee Na Saw as a need
      • Level 1 - Scratching the Surface
    • SPbCTF's Student CTF Quals
      • 31 Line PHP
      • BLT
      • CatStep
    • Asian Cyber Security Challenge (ACSC) 2021
      • Cowsay As A Service
      • Favorite Emojis
      • Baby Developer
      • API
      • RSA Stream
      • Filtered
      • NYONG Coin
    • CSAW CTF Qualification Round 2021
      • Save the Tristate
      • securinotes
      • no pass needed
      • Gatekeeping
      • Ninja
    • YauzaCTF 2021
      • Yauzacraft Pt. 2
      • Yauzabomber
      • RISC 8bit CPU
      • ARC6969 Pt. 1
      • ARC6969 Pt. 2
      • Back in 1986 - User
      • Lorem-Ipsum
    • InCTF 2021
      • Notepad 1 - Snakehole's Secret
      • RaaS
      • MD Notes
      • Shell Boi
      • Listen
      • Ermittlung
      • Alpha Pie
    • UIUCTF 2021
      • pwnies_please
      • yana
      • ponydb
      • SUPER
      • Q-Rious Transmissions
      • capture the :flag:
      • back_to_basics
      • buy_buy_buy
    • Google CTF 2021
      • CPP
      • Filestore
    • TyphoonCon CTF 2021
      • Clubmouse
      • Impasse
    • DSTA BrainHack CDDC21
      • File It Away (Pwn)
      • Linux Rules the World! (Linux)
      • Going Active (Reconnaissance)
      • Behind the Mask (Windows)
      • Web Takedown Episode 2 (Web)
      • Break it Down (Crypto)
    • BCACTF 2.0
      • L10N Poll
      • Challenge Checker
      • Discrete Mathematics
      • Advanced Math Analysis
      • Math Analysis
      • American Literature
      • More Than Meets the Eye
      • 􃗁􌲔􇺟􊸉􁫞􄺷􄧻􃄏􊸉
    • Zh3ro CTF V2
      • Chaos
      • Twist and Shout
      • 1n_jection
      • alice_bob_dave
      • Baby SSRF
      • bxxs
      • Sparta
    • Pwn2Win CTF 2021
      • C'mon See My Vulns
      • Illusion
    • NorzhCTF 2021
      • Leet Computer
      • Secure Auth v0
      • Triskel 3: Dead End
      • Triskel 2: Going In
      • Triskel 1: First Contact
      • Discovery
    • DawgCTF 2021
      • Bofit
      • Jellyspotters
      • No Step On Snek
      • Back to the Lab 2
      • MDL Considered Harmful
      • Really Secure Algorithm
      • The Obligatory RSA Challenge
      • Trash Chain
      • What the Flip?!
      • Back to the Lab 1
      • Back to the Lab 3
      • Dr. Hrabowski's Great Adventure
      • Just a Comment
      • Baby's First Modulation
      • Two Truths and a Fib
    • UMDCTF 2021
      • Advantageous Adventures
      • Roy's Randomness
      • Whose Base Is It Anyway
      • Cards Galore
      • Pretty Dumb File
      • Minetest
      • Donnie Docker
      • Subway
      • Jump Not Easy
      • To Be XOR Not To Be
      • Office Secrets
      • L33t M4th
      • Bomb 2 - Mix Up
      • Jay
    • Midnight Sun CTF 2021
      • Corporate MFA
      • Gurkburk
      • Backups
    • picoCTF 2021
      • It Is My Birthday (100)
      • Super Serial (130)
      • Most Cookies (150)
      • Startup Company (180)
      • X marks the spot (250)
      • Web Gauntlet (170 + 300)
      • Easy Peasy (40)
      • Mini RSA (70)
      • Dachshund Attacks (80)
      • No Padding, No Problem (90)
      • Trivial Flag Transfer Protocol (90)
      • Wireshark twoo twooo two twoo... (100)
      • Disk, Disk, Sleuth! (110 + 130)
      • Stonks (20)
    • DSO-NUS CTF 2021
      • Insecure (100)
      • Easy SQL (200)
Powered by GitBook
On this page
  • Simple Solution
  • Solving with Minimax

Was this helpful?

  1. 2022
  2. CTF.SG CTF

Chopsticks

PreviousWildest Dreams Part 2NextYaCTF 2022

Last updated 3 years ago

Was this helpful?

Both challenges are based on the , but with twists on the rules.

Here are the rules for the first game:

+-------------------------------------------------+
| Rules:                                          |
| This game is similar to the game Chopsticks:    |
|   en.wikipedia.org/wiki/Chopsticks_(hand_game)  |
|                                                 |
| * Each person starts with two boxes, with 1     |
|   feather.                                      |
| * If any box has at least 1 feather, it is live |
| * If any box has no feathers, it is dead        |
| * If any box contains more than 6 feathers, it  |
|   is dead, and has all its feathers taken out.  |
| * If a player has both boxes dead, the player   |
|   loses.                                        |
| * At each turn, a player can either:            |
|   * Attack: Add all feathers from one box to    |
|     another (their own or another player's)     |
|       * You can't attack to and from a dead box |
|   * Split: Split the feathers between their     |
|     own boxes.                                  |
|       * A split that results in one box having  |
|         one or zero feathers is not allowed.    |
|       * A split can only happen if both boxes   |
|         are live.                               |
| * Loops are disallowed                          |
|   * The game will prevent you from entering a   |
|     state already visited.                      |
+-------------------------------------------------+

And the second:

+-------------------------------------------------+
| Rules:                                          |
| This game is similar to the game Chopsticks:    |
|   en.wikipedia.org/wiki/Chopsticks_(hand_game)  |
|                                                 |
| * Each person starts with two boxes, with 1     |
|   feather.                                      |
| * If any box has at least 1 feather, it is live |
| * If any box has no feathers, it is dead        |
| * If any box contains more than 6 feathers, it  |
|   is dead, and has all its feathers taken out.  |
| * If a player has both boxes dead, the player   |
|   loses.                                        |
| * At each turn, a player can either:            |
|   * Attack: Add all feathers from one box to    |
|     another (their own or another player's)     |
|       * You can't attack to and from a dead box |
|   * Split: Split the feathers between their     |
|     own boxes.                                  |
|       * A split that results in one box having  |
|         zero feathers is not allowed.           |
|       * A split can happen if one of the boxes  |
|         is dead, meaning a split can revive a   |
|         box.                                    |
| * Loops are disallowed                          |
|   * The game will prevent you from entering a   |
|     state already visited.                      |
+-------------------------------------------------+

Simple Solution

It seems like many teams managed to solve both challenges by pitting the bot against itself. Since this is a solved game, two equally maximizing bots playing against each other will likely lead to the first player winning.

I didn't think of that for some reason...

Solving with Minimax

What I did was write my own bot to play against the server's bot. Since the server's bot was likely using a similar algorithm as mine, I just had to increase the minimax depth until my bot could perform sufficient lookaheads to play better than the server's bot.

Surprisingly I only needed a depth of 5 to win and didn't need to implement alpha-beta pruning since it was returning a result fast enough.

The evaluation score is 1000 if we win, -1000 if we lose, and the difference between the total number of our feathers and the total number of the opponent's feathers otherwise.

def get_score(board, player, depth):
    """Use the minimax algorithm to search up to <depth>"""

    winner = get_winner(board)
    if winner:
        if winner == 1:     # Maximizing player
            return 1000
        else:
            return -1000

    if depth == 0:
        return board['A'] + board['B'] - board['C'] - board['D']
    
    attacks = available_attacks(board, player)
    splits = available_splits(board, player)
    if not attacks and not splits:
        return 0

    max_value = -1000
    min_value = 1000

    for attack in attacks:
        temp_board = board.copy()
        from_hand, to_hand = attack

        temp_board[to_hand] += temp_board[from_hand]
        if temp_board[to_hand] > 6:
            temp_board[to_hand] = 0     # die

        value = get_score(temp_board, 3 - player, depth - 1)

        if player == 1:
            # Maximizing player
            max_value = max(max_value, value)
        else:
            # Minimizing player
            min_value = min(min_value, value)
    
    for split in splits:
        temp_board = board.copy()
        left, right = split

        if player == 1:
            temp_board['A'] = left
            temp_board['B'] = right
        
        else:
            temp_board['C'] = left
            temp_board['D'] = right

        value = get_score(temp_board, 3 - player, depth - 1)
        
        if player == 1:
            # Maximizing player
            max_value = max(max_value, value)
        else:
            # Minimizing player
            min_value = min(min_value, value)

    if player == 1:
        return max_value
    else:
        return min_value

We also had to account for the fact that the sever prevents us from returning to a previously visited game state, so we will keep track of visited states on our end as well.

def get_best_move(board):
    
    # We are player 1
    
    attacks = available_attacks(board, 1)
    splits = available_splits(board, 1)

    max_value = -1000
    max_move = []

    for attack in attacks:
        temp_board = board.copy()
        from_hand, to_hand = attack

        temp_board[to_hand] += temp_board[from_hand]
        if temp_board[to_hand] > 6:
            temp_board[to_hand] = 0

        if temp_board in visited:
            continue

        value = get_score(temp_board, 2, DEPTH)

        if value > max_value:
            max_value = value
            max_move = ['attack', attack]

    for split in splits:
        temp_board = board.copy()
        left, right = split

        temp_board['A'] = left
        temp_board['B'] = right

        if temp_board in visited:
            continue

        value = get_score(temp_board, 2, DEPTH)

        if value > max_value:
            max_value = value
            max_move = ['split', split]

    return max_move

Sorry for the repeated and inefficient code, I was stressed and just trying to get it to work

😢
Chopsticks game