For this challenge, when we connect to the server we’re presented with a Connect 4 board.

You can input the numbers 1-6 for the respective column and it will place your marker on the board. If you take too long you’re presented with the following message.


You took too long! You only had 30 seconds to beat me this time!

The first round was easily won by just doing “3->2->1->0”.

The next round was relatively easy as well for manual completion. In the round after that, you immediately see the computer starting to block your moves and requiring you to spend more time analyzing the board for the next placement.

Then we receive the following message.


You took too long! You only had 10 seconds to beat me this time!

At minimum it seems like we’ll need to write a program that will play the game (and win) to progress through until we get the key.

Instead of just posting code (which can be found here), I’m going to talk to the strategy I took to play the game. Basically, there are 3 ways you can win connect four. You either get 4 in a row horizontally, vertically, or diagonally.

After each move, the script I wrote would scan the board, line by line, and determine where each “x” or “o” is. Then this data is subsequently fed to a function that tries to determine the next candidate move.

I decided it would be best to try and play defensively at first, before playing offensively, so I wrote functions that would look for scenarios where the computer has 3 markers in any of the solving directions (row/column/front or back slant). If it detects one, it will place a marker in the column that blocks their next move. I kept the logic fairly simple and did not account for more advanced scenarios, such as where the next move might win if the 3 markers are currently separated. For example, a marker in column 1, 3, and 4 would not trigger it to place in column 2, even if that next win would move.

Just using the defensive functions and generating a random column to place in, assuming no blocks were necessary, was enough to get into the 4^th^ level every few tries.

For the offensive functions, I took a similar approach to defense and looked for the same three scenarios but just for my markers. I also ended up re-arranging the functions slightly so that diagonal scenarios were considered first as the “late” game moves almost always seemed to be won with those moves.

Doing this I was able to reliably get into the 4^th^ level of the board and regularly entered into the 5^th^ level; however, after 500+ runs, I was still unable to beat it.

One thing I noticed was that during the 4^th^ and 5^th^ iterations, the time it takes the computer to return their moves was extremely long and usually the loss was due to hitting the time limit.

The GIF below illustrates the issue.

As you can see, in the 5^th^ level it slows to a snails pace. What I found was that the server appears to be calculating its moves each time; however, on subsequent runs the same moves will be instant so they seem cached.

If you look at the next GIF, you’ll note that in the 5^th^ level it’s exceptionally faster

What this means is that we can basically record the moves that get us to the 5th round and then replay them. Since it takes too long to actually play here, we’ll have to just iterate over the content and train the game for our moves so subsequent plays are faster and we can progress further.

To do this, I added a small loop to the script that will execute the commands in order and then when it hits the 5th level, it will default back to the logic we created before, forcing it to play until timeout, win, or loss. This gives a glimpse at potential future moves and how the computer will respond, so we can also adjust our gameplay accordingly.


moveList = ["3", "2", "4", "6", "1", # Lvl 2
            "3", "2", "4", "6", "1", # Lvl 3
            "3", "2", "1", "2", "3", "1", "1", "2", "2", # Lvl 4
            "2", "4", "3", "2", "1", "4", "6", "2", "1", "1", "3", "2", "5", "4"] # Lvl 5

for move in moveList:

    time.sleep(0.3)

    sock.send(move)

    data = sock.recv(1024)

    print data

From here on out, I just played Connect 4 against the computer and relived repressed memories of losing constantly at Connect 4…except now I had the upper hand! Luckily, it’s easy to delete a move from the list and pretend no one saw all of the horrible mistakes I made.

Eventually a winning pattern emerges and we can replay it to gain our key.

The script “c4_solver.py” can be downloaded from here. (INSERT LINK TO THE FILE FOR BLOG)

The challenge answer is “PAN{0ld_g4mz_4r3_b3st_g4mz}”.