def score4_evaluation_function(game_state, agent, **context):
    """
    This evaluation function comes from Score4 project:
    https://github.com/ttsiodras/Score4

    "Speaking of the scoreBoard function, I tried various forms to evaluate the
     board. I ended up on a simple policy: measuring how many chips of the same
     color exist, in spans of 4 going in any direction. I do this over each of
     the board's cells, and then aggregate this in a table keeping the
     aggregates from -4 to 4:

     -4 means that the cell is a part of 4 cells that contain 4 yellow chips
     -3 means that the cell is a part of 4 cells that contain 3 yellow chips
     ...
     3 means that the cell is a part of 4 cells that contain 3 orange chips
     4 means that the cell is a part of 4 cells that contain 4 orange chips 

     If 4 is found, the board is a win for the Orange player, and the function
     returns orangeWins (i.e. 1000000). If -4 is found, the board is a win for
     the Yellow player, and the function returns yellowWins (i.e. -1000000).
     Otherwise, scaling factors are applied, so that the more '3'-cells found,
     the more positive the board's score. Correspondingly, the more '-3' found,
     the more negative the board's score."
    """
    agent_index = agent.get_index()

    counters = [0] * 9

    board = game_state.get_board()
    # Horizontal spans
    for r in range(board.height()):
        score = 0
        for k in range(3):
            if board.has_token(k, r):
                if board.get_token(k, r) == agent_index:
                    score += 1
                else:
                    score -= 1
        for c in range(3, board.width()):
            if board.has_token(c, r):
                if board.get_token(c, r) == agent_index:
                    score += 1
                else:
                    score -= 1
            counters[int(score) + 4] += 1
            if board.has_token(c-3, r):
                if board.get_token(c-3, r) == agent_index:
                    score -= 1
                else:
                    score += 1

    # Vertical spans
    for c in range(board.width()):
        score = 0
        for k in range(3):
            if board.has_token(c, k):
                if board.get_token(c, k) == agent_index:
                    score += 1
                else:
                    score -= 1
        for r in range(3, board.height()):
            if board.has_token(c, r):
                if board.get_token(c, r) == agent_index:
                    score += 1
                else:
                    score -= 1
            counters[score + 4] += 1
            if board.has_token(c, r-3):
                if board.get_token(c, r-3) == agent_index:
                    score -= 1
                else:
                    score += 1

    # Down-right (and up-left) diagonals
    for r in range(board.height()-3):
        for c in range(board.width()-3):
            score = 0
            for k in range(4):
                rr = r + k
                cc = c + k
                if board.has_token(cc, rr):
                    if board.get_token(cc, rr) == agent_index:
                        score += 1
                    else:
                        score -= 1
            counters[score + 4] += 1

    # up-right (and down-left) diagonals
    for r in range(3,board.height()):
        for c in range(board.width()-3):
            score = 0
            for k in range(4):
                rr = r - k
                cc = c + k
                if board.has_token(cc, rr):
                    if board.get_token(cc, rr) == agent_index:
                        score += 1
                    else:
                        score -= 1
            counters[score + 4] += 1

    max_score = 1000000
    score = 0.0

    if counters[0] != 0:
        score = -max_score
    elif counters[8] != 0:
        score = max_score
    else:
        score = (counters[5] + 2*counters[6] + 5*counters[7] - counters[3] - 2*counters[2] - 5*counters[1])

    score /= max_score

    print("Agent: ", agent_index, ", State: ", game_state, ", Counter: ", counters, " =>  Score: ", score)

    return score