Skip to content

Instantly share code, notes, and snippets.

@LieBtrau
Last active August 23, 2024 19:41
Show Gist options
  • Save LieBtrau/88924083a531d99251664d0616129b31 to your computer and use it in GitHub Desktop.
Save LieBtrau/88924083a531d99251664d0616129b31 to your computer and use it in GitHub Desktop.
Songka game Killjoy: finds a sequence of moves so that the player that goes first always wins.
#!/usr/bin/env python3
# Songka game solver
# [Game Rules](https://www.wikihow.com/Play-Congkak)
# [Songka_Plus on Android](https://play.google.com/store/apps/details?id=net.azurewebsites.sungka.twa&hl=en_US)
# This is pretty much a solved game. The player that goes first will always win if he plays optimally.
# There are several sequences to achieve this. The following is one of them:
# Solution found: 41 turns, 93 seeds in the storehouse
# 0 1 4 1 0 4
# 1 1 2 3 2 0
# 1 2 0 1 6 1
# 6 5 5 0 6 6
# 3 0 6 1 4 6
# 6 0 6 1 6 5
# 6 2 6 4 6
# Where 0 is the first house on the player's side (farthest from player's store house), 1 is the second house on the player's side, etc.
# As the opponent is only left with 5 pieces at the end of the game, all opponent's houses are burnt. Game over.
import copy, sys
opponent_storehouse = 7
player_storehouse = 15
maximum_score = 0
minimum_turns = 100
turns = [0 for i in range(100)]
def play():
# Play the game
# Board is arranged in clockwise direction as follows:
# board[0] to board[6] = houses on opponent side
# board[7] = opponent's storehouse
# board[8] to board[14] = houses on player side
# board[15] = player's storehouse
# board[0] is opposite to board[14], board[1] is opposite to board[13], etc.
board = [0 for i in range(16)]
# Prepare the board, fill the houses with 7 seeds each. The storehouses are empty.
for i in range(16):
if i != opponent_storehouse and i != 15:
board[i] = 7
play_opponent(0, board)
def string_board(board):
# Convert the board to a string
# board = current board
s = "\t"
for i in range(8):
s += str(board[i]) + "\t"
s += "\n" + str(board[player_storehouse]) + "\t"
for i in range(14, 7, -1):
s += str(board[i]) + "\t"
s += "\n"
return s
def play_opponent(level, board):
# Find the solution by playing the opponent's turn
for position in range(7):
global turns
turns[level] = position
board_copy = copy.deepcopy(board)
out_board, playing = play_turn(0, board_copy, position)
# if(playing):
# for j in range(level):
# print(" ", end="")
# print(level, position, out_board[opponent_storehouse], playing)
# print(string_board(out_board))
if(playing):
# The opponent gets another turn
# Check if opponent houses are all empty
empty = True
for i in range(7):
if out_board[i] > 0:
empty = False
break
if empty:
# Game over
global minimum_turns
if(level + 1 < minimum_turns):
minimum_turns = level + 1
global maximum_score
maximum_score = out_board[opponent_storehouse]
print("New minimum turns: ", minimum_turns, "New maximum score: ", maximum_score)
print("Solution found: ", level+1, "turns, ", out_board[opponent_storehouse], "seeds in the storehouse")
for i in range(level+1):
print(turns[i], end=" ")
if((i+1)%6 ==0):
print()
print()
else:
play_opponent(level + 1, out_board)
def play_turn(player, board, house):
# Play one turn
# player = 0 for opponent, 1 for player
# board = current board
# house = house number
# Return board after the turn, and True if the player gets another turn, False otherwise
# Check for invalid house
if(player == 0 and (house < 0 or house > 6)):
print("Invalid house number")
return board, False
if(player == 1 and (house < 8 or house > 14)):
print("Invalid house number")
return board, False
# Distribute the seeds
while(board[house] > 0):
board, house = distribute_seeds(player, house, board)
# print(string_board(board))
if(player == 0 and house == opponent_storehouse):
# The last seed is placed in the storehouse
return board, True
if(player == 1 and house == player_storehouse):
# The last seed is placed in the storehouse
return board, True
if(board[house] == 1):
# The last seed is placed in an empty house
# If the player ends in an empty house on his side, he captures the seeds in the opposite house if there are seeds in it.
if(((player == 0 and house < 7)) or (player == 1 and house > 7)) and board[14 - house] > 0:
# Capture the seeds
board[opponent_storehouse if player==0 else player_storehouse] += board[14 - house] + 1
board[14 - house] = 0
board[house] = 0
return board, False
return board, False
def distribute_seeds(player, house, board):
# Distribute seeds from a house
# player = 0 for opponent, 1 for player
# house = house number
# board = current board
# Return the new board and the house number where the last seed is placed
house = int(house)
if(house == opponent_storehouse or house == player_storehouse):
print("The house is a storehouse, nothing to distribute")
return board, -1
if(house < 0 or house > 15):
print("Invalid house number")
return board, -1
# Get the number of seeds in the house
seeds = board[house]
if(seeds == 0):
# The house is empty, nothing to distribute
return board, house
# Empty the house
board[house] = 0
# Distribute the seeds
for i in range(seeds):
house = (house + 1) % 16
if(player == 0 and house == player_storehouse):
# Skip player's storehouse
house = 0
if(player == 1 and house == opponent_storehouse):
# Skip opponent's storehouse
house = 8
board[house] += 1
return board, house
def main(argv):
# Find the solution
play()
if __name__ == '__main__':
main(sys.argv[1:])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment