comp-think / 2020-2021

The GitHub repository containing all the material related to the Computational Thinking and Programming course of the Digital Humanities and Digital Knowledge degree at the University of Bologna (a.a. 2020/2021).
14 stars 9 forks source link

Bonus exercise (AtariGo) #33

Open essepuntato opened 3 years ago

essepuntato commented 3 years ago

Implement the function below in Python, that takes in input the colour of the player who has to play the turn (parameter colour), the sets of coordinates (i.e. sets of tuples) of all the black stones (parameter black) and white stones (parameter white) already positioned on the board, and returns the x, y coordinate (a tuple) of a free intersection where to place a new colour stone. The coordinates of the various positions of the board are those ones defined in "the board" as in the AlphaGo material available online.

def place_stone(colour, black, white):
    # study the board and calculate the
    # best place where to position the stone
    return x, y # the coordinates of the new stone

Additional information is available at https://doi.org/10.5281/zenodo.2204836.

AlessandroBertozzi commented 3 years ago

I don't really understand how to teach the machine to get the best solution. Nevertheless, my function return randomly one of the possibile position near to the opposite colour. The logic is to close the enemy stone.

import random

def place_stone(colour, black, white):
    if colour == "black":
        place = random.choice(possible_move("black", black, white))
        return place
    elif colour == "white":
        place = random.choice(possible_move("white", black, white))
        return place

def possible_move(colour, black, white): # check the free position near the opposite color
    if colour == "black":
        movement_list = []
        for position in white:
            list_single_move = single_stone(position, black, white)
            movement_list.extend(list_single_move)
        return movement_list
    elif colour == "white":
        movement_list = []
        for position in black:
            list_single_move = single_stone(position, black, white)
            movement_list.extend(list_single_move)
        return movement_list

def single_stone(position, black, white): # check the free position near the stone and return a list of free position
    list_movement = []
    if (position[0] - 1, position[1]) not in black and (position[0] - 1, position[1]) not in white and position[0] - 1 <= 6:
        list_movement.append((position[0] - 1, position[1]))
    if (position[0] + 1, position[1]) not in black and (position[0] + 1, position[1]) not in white and position[0] + 1 <= 6:
        list_movement.append((position[0] + 1, position[1]))
    if (position[0], position[1] - 1) not in black and (position[0], position[1] - 1) not in white and position[1] - 1 <= 6:
        list_movement.append((position[0], position[1] - 1))
    if (position[0], position[1] + 1) not in black and (position[0], position[1] + 1) not in white and position[1] + 1 <= 6:
        list_movement.append((position[0], position[1] + 1))
    return list_movement

print(place_stone("white", {(1, 5), (1, 4), (2, 6)}, {(2, 5), (2, 4)}))
diegochillo commented 3 years ago

This function classifies all the free points on the board in these categories:

Then, to select the right move to return, it privileges:

  1. The first position that limits opponents and expand the current color, if one is found;
  2. Else, the first position that expands current color's liberties, if one is found;
  3. Else, the first position that limits opponent's liberties, if one is found;
  4. Else, a position that is not adjacent to anything. Otherwise there are no free positions to return.

Not a great strategy, but a lot of thoughts about liberties and groups and others and what is better to pursue.

def place_stone(color,black,white):

  if len(black & white)==0:     # Check that black and white sets don't have common elements

    range_x=range(7)            # Maximum number of columns
    range_y=range(7)            # Maximum number of rows
    occupied=black.union(white) # Defines a set with all the occupied cells regardless of the color

    expanding=list()  # Prepares the list of moves that expand the liberties
    limiting=list()   # Prepares the list of moves that limit opponent's liberties 
    others=list()     # Prepres the list of all other positions

    # Gets rid of the black/white dialectics and switches to current/opponent
    if color=="white":
      current=white
      opponent=black
    else:
      current=black
      opponent=white

    # Cycles all the positions of the board:
    for x in range_x:
      for y in range_y:

        if ((x,y) not in occupied): # if position is empty

          if check_neighbors(x,y,opponent): # if position limits opponent liberties
            limiting.append((x,y))
          if check_neighbors(x,y,current):  # if position expands current color liberties
            expanding.append((x,y))
          if ((x,y) not in limiting) and ((x,y) not in expanding): # if position has no neighbor stones
            others.append((x,y))

    bestpos=set(limiting) & set(expanding) # Finds the positons limiting the opponent and expanding the current group

    if len(bestpos)>0:
      return(list(bestpos)[0]) # Returns the first result that expand your liberty and limits the opponent

    if len(limiting)>0:
      return(limiting[0])      # Else returns the first result that limits the opponent

    if len(expanding)>0:
      return expanding[0]      # Else returns the first result that expands your liberties

    if len(others)>0:
      return (others[0])       # Else returns the first free position 

    return "No position available"  # Else no move can be done

  else:   
    return "Each point admits only one stone!"

# This ancillary function only returns if x,y position is adjacent to something in checking set
def check_neighbors(x,y,checking):
  return ((x-1,y) in checking) or ((x+1,y) in checking) or ((x,y-1) in checking) or ((x,y+1) in checking)

# TEST CASES  

color="white"
black={(2,2),(2,3),(2,4),(2,5)}
white={(2,1),(3,2),(3,3)}
print (place_stone(color,black,white)) # Answers (3,4)

color="black"
black={(4,3),(4,4),(4,5),(5,3),(5,5),(6,3),(6,5)}
white={(5,4)}
print (place_stone(color,black,white)) # Answers (6,4)... My boy!

color="black"
black={(4,3),(4,4),(4,5),(5,3),(5,5),(6,3),(6,5)}
white={(4,3)}
print (place_stone(color,black,white)) # Returns an error