Example #1
0
    def drawLine(self, lines):
        """ 
        This is called when it's this player's turn to draw a line.
        This is the method to implement by the student.
        This method provides the lines that are currently drawn
        in the game board. 

        Each line in lines is a list of ints in this format:
        [m1, n1, m2, n2]
        The above cartesian coordinates correspond to the game board:
        The gameboard is an imaginary object that has 
        the following layout:
        --------------------------> m
        | (0,0) (0,1) (0,2) (0,3)   
        | (1,0) (1,1) (1,2) (1,3)   
        | (2,0) (2,1) (2,2) (2,3)   
        | (3,0) (3,1) (3,2) (3,3)   
        n (grows downwards)

        e.g. lines = [ [0,0, 0,1], [1,0, 0,1], [0,0, 1,0], ]
            this contains 3 lines that forms a triangle 

        * Note that lines can be an empty list []
        * There are 42 possible lines, 33 of which will be used
            in a single game (discounting the overlapping lines)
        
        all_possible_valid_lines = [
            # all horizontal lines
            [0,0, 0,1], [0,1, 0,2], [0,2, 0,3], [1,0, 1,1],
            [1,1, 1,2], [1,2, 1,3], [2,0, 2,1], [2,1, 2,2],
            [2,2, 2,3], [3,0, 3,1], [3,1, 3,2], [3,2, 3,3],

            # all vertical lines
            [0,0, 1,0], [0,1, 1,1], [0,2, 1,2], [0,3, 1,3],
            [1,0, 2,0], [1,1, 2,1], [1,2, 2,2], [1,3, 2,3],
            [2,0, 3,0], [2,1, 3,1], [2,2, 3,2], [2,3, 3,3],

            # all positive slopes
            [1,0, 0,1], [1,1, 0,2], [1,2, 0,3], [2,0, 1,1],
            [2,1, 1,2], [2,2, 1,3], [3,0, 2,1], [3,1, 2,2],
            [3,2, 2,3], 

            # all negative slopes
            [0,0, 1,1], [0,1, 1,2], [0,2, 1,3], [1,0, 2,1],
            [1,1, 2,2], [1,2, 2,3], [2,0, 3,1], [2,1, 3,2],
            [2,2, 3,3],
        ]

        ** This method needs to return a valid, non-overlapping 
            line or you lose. So, you should definitely check the
            validity of the line first before returning it to avoid
            disqualification. Do this by:

            if not line_overlaps(my_line, lines) and\
                line_is_valid(my_line):
                # fix it. It should never go here if you know what you
                # are doing.
            return my_line

        1) Use line_is_valid(line) to check if the line follows 
        the convention below.
        
        As a convention of the game, lines should be    
        written from left to right and from up to down
        e.g. 
        VALID 
            vertical lines: '0010', '1121', ...
            horizontal lines: '0001', '1112', ...
            positive slope: '1001', '1203', ...
            negative slope: '0011', '1223', ...
            
        INVALID 
            vertical lines: '1000', '2111', ...
            horizontal lines: '0100', '1211', ...
            positive slope: '0110', '0312', ...
            negative slope: '1100', '2312', ...

        2) Use line_overlaps(line, lines) to check if line is already
            drawn (already in lines) and if it overlaps another line.
        """
        all_lines = self.all_possible_valid_lines
        
        # choose a random line
        chosen_line_index = randint(0, len(all_lines)-1)
        chosen_line = all_lines[chosen_line_index]
        
        # if the line is invalid, remove it and choose another
        if all_lines: # not empty
            while line_overlaps(chosen_line, lines) or\
                not line_is_valid(chosen_line):
                all_lines.remove(chosen_line)
                chosen_line_index = randint(0, len(all_lines)-1)
                chosen_line = all_lines[chosen_line_index]

        return chosen_line     
    def drawLine(self, lines):
        """
        Strategy -----------------------------------------------------
        Step 1: check for incomplete triangles in lines
            - if there is such a triangle, then complete it.
        Step 2: if step 1 does not return a line, choose a line that
                will not leave in an incomplete triangle for the other
                player after this turn. 
        Step 3: if step 2 does not return a line, choose a line that
                will result in an incomplete triangle but will result
                in the least amount of triangles being completed
                by the other player in the following turn.
        Notes --------------------------------------------------------
        The gameboard is an imaginary object that has 
        the following layout:
        --------------------------> m
        | (0,0) (0,1) (0,2) (0,3)   
        | (1,0) (1,1) (1,2) (1,3)   
        | (2,0) (2,1) (2,2) (2,3)   
        | (3,0) (3,1) (3,2) (3,3)   
        n (grows downwards)
        """
        # first turn of the game, choose arbitrary line.
        if not lines:
            return [0, 0, 0, 1] 
        # update the list of lines that are not yet drawn
        lines_to_rmv = []
        for line in self.all_possible_valid_lines:
            if line_overlaps(line, lines) or\
                not line_is_valid(line):
                lines_to_rmv.append(line)
        for l in lines_to_rmv:
            self.all_possible_valid_lines.remove(l)

        # if step 1 fails, do not let opponent predict what
        # we will draw        
        shuffle(self.all_possible_valid_lines)

        # Step 1
        c = self.line_complete(lines)
        if c:
            return c[0]
        # Step 2
        for line in self.all_possible_valid_lines:
            tmp = lines[:]
            tmp.append(line)
            c = self.line_complete(tmp)
            if not c:
                return line
        # Step 3
        min_score, l = 9001, None
        for line in self.all_possible_valid_lines:
            score = 0
            tmp = lines[:]
            tmp.append(line)
            while True:
                c = self.line_complete(tmp)
                if c:
                    score += c[1]
                    tmp.append(c[0])
                    continue
                break
            if score < min_score or not l:
                min_score = score
                l = line

        return l