def line_complete(self, lines):
        """
        Checks for an incomplete triangle in lines and returns the
        line that will make it complete as well as the number of
        triangles that will be completed (1 or 2).
        Note that since I know that the lines I create are valid,
        I do not use line_is_valid in this method.
        """
        for l in lines:
            ### line is horizontal
            if l[0] == l[2]:
                # UPPER
                l_neg = [l[0]-1, l[1], l[2], l[3]]
                l_pos = [l[0], l[1], l[0]-1, l[1]+1]
                l_v = [l[0]-1, l[1], l[0], l[1]]
                r_v = [l[2]-1, l[3], l[2], l[3]]
                # negative slope exist
                if l_neg in lines and not line_overlaps(l_v, lines):
                    if self.incomplete_exist(l_v, lines, "ver_left"):
                        return l_v, 2
                    return l_v, 1
                # positive slope exist
                elif l_pos in lines and not line_overlaps(r_v, lines):
                    if self.incomplete_exist(r_v, lines, "ver_right"):
                        return r_v, 2
                    return r_v, 1
                # left vertical line exist
                elif l_v in lines and not line_overlaps(l_neg, lines):
                    if self.incomplete_exist(l_neg, lines, "neg_top"):
                        return l_neg, 2
                    return l_neg, 1
                # right vertical line exist
                elif r_v in lines and not line_overlaps(l_pos, lines):
                    if self.incomplete_exist(l_pos, lines, "pos_top"):
                        return l_pos, 2
                    return l_pos, 1

                # LOWER
                l_neg = [l[0], l[1], l[0]+1, l[1]+1]
                l_pos = [l[0]+1, l[1], l[2], l[3]]
                l_v = [l[0], l[1], l[0]+1, l[1]]
                r_v = [l[2], l[3], l[2]+1, l[3]]
                # negative slope exist
                if l_neg in lines and not line_overlaps(r_v, lines):
                    if self.incomplete_exist(r_v, lines, "ver_right"):
                        return r_v, 2
                    return r_v, 1
                # positive slope exist
                elif l_pos in lines and not line_overlaps(l_v, lines):
                    if self.incomplete_exist(l_v, lines, "ver_left"):
                        return l_v, 2
                    return l_v, 1
                # left vertical line exist
                elif l_v in lines and not line_overlaps(l_pos, lines):
                    if self.incomplete_exist(l_pos, lines, "pos_bot"):
                        return l_pos, 2
                    return l_pos, 1
                # right vertical line exist
                elif r_v in lines and not line_overlaps(l_neg, lines):
                    if self.incomplete_exist(l_neg, lines, "neg_bot"):
                        return l_neg, 2
                    return l_neg, 1

            # if line is vertical
            elif l[1] == l[3]:
                # LEFT
                l_neg = [l[0], l[1]-1, l[2], l[3]]
                l_pos = [l[2], l[3]-1, l[0], l[1]]
                t_h = [l[0], l[1]-1, l[0], l[1]]
                b_h = [l[2], l[3]-1, l[2], l[3]]
                # negative slope exist
                if l_neg in lines and not line_overlaps(t_h, lines):
                    if self.incomplete_exist(t_h, lines, "hor_top"):
                        return t_h, 2
                    return t_h, 1
                # positive slope exist
                elif l_pos in lines and not line_overlaps(b_h, lines):
                    if self.incomplete_exist(b_h, lines, "hor_bot"):
                        return b_h, 2
                    return b_h, 1
                # top horizontal line exist
                elif t_h in lines and not line_overlaps(l_neg, lines):
                    if self.incomplete_exist(l_neg, lines, "neg_bot"):
                        return l_neg, 2
                    return l_neg, 1
                # bottom horizontal line exist
                elif b_h in lines and not line_overlaps(l_pos, lines):
                    if self.incomplete_exist(l_pos, lines, "pos_top"):
                        return l_pos, 2
                    return l_pos, 1

                # RIGHT
                l_neg = [l[0], l[1], l[2], l[3]+1]
                l_pos = [l[2], l[3], l[0], l[1]+1] 
                t_h = [l[0], l[1], l[0], l[1]+1]
                b_h = [l[2], l[3], l[2], l[3]+1] 
                # negative slope exist
                if l_neg in lines and not line_overlaps(b_h, lines):
                    if self.incomplete_exist(b_h, lines, "hor_bot"):
                        return b_h, 2
                    return b_h, 1
                # positive slope exist    
                elif l_pos in lines and not line_overlaps(t_h, lines):
                    if self.incomplete_exist(t_h, lines, "hor_top"):
                        return t_h, 2
                    return t_h, 1
                # top horizontal line exist
                elif t_h in lines and not line_overlaps(l_pos, lines):
                    if self.incomplete_exist(l_pos, lines, "pos_bot"):
                        return l_pos, 2
                    return l_pos, 1
                # bottom horizontal line exist
                elif b_h in lines and not line_overlaps(l_neg, lines):
                    if self.incomplete_exist(l_neg, lines, "neg_top"):
                        return l_neg, 2
                    return l_neg, 1

            # Note that unlike the vertical and horizontal lines,
            # slopes can only have 2 possible triangles

            # if line is a positive slope /
            elif l[2]-l[0] < 0 and l[3]-l[1] > 0:
                # TOP
                t_h = [l[2], l[3]-1, l[2], l[3]]
                l_v = [l[0]-1, l[1], l[0], l[1]]
                if t_h in lines and not line_overlaps(l_v, lines):
                    if self.incomplete_exist(l_v, lines, "ver_left"):
                        return l_v, 2
                    return l_v, 1
                elif l_v in lines and\
                        not line_overlaps(t_h, lines):
                    if self.incomplete_exist(t_h, lines, "hor_top"):
                        return t_h, 2
                    return t_h, 1
                # BOTTOM
                b_h = [l[0], l[1], l[0], l[1]+1]
                r_v = [l[2], l[3], l[2]+1, l[3]]
                if b_h in lines and not line_overlaps(r_v, lines):
                    if self.incomplete_exist(r_v, lines, "ver_right"):
                        return r_v, 2
                    return r_v, 1
                elif r_v in lines and\
                        not line_overlaps(b_h, lines):
                    if self.incomplete_exist(b_h, lines, "hor_bot"):
                        return b_h, 2
                    return b_h, 1

            # if line is a negative slope \
            elif l[2]-l[0] > 0 and l[3]-l[1] > 0:
                # TOP
                t_h = [l[0], l[1], l[0], l[1]+1]
                r_v = [l[2]-1, l[3], l[2], l[3]]
                if t_h in lines and not line_overlaps(r_v, lines):
                    if self.incomplete_exist(r_v, lines, "ver_right"):
                        return r_v, 2
                    return r_v, 1
                elif r_v in lines and\
                        not line_overlaps(t_h, lines):
                    if self.incomplete_exist(t_h, lines, "hor_top"):
                        return t_h, 2
                    return t_h, 1
                # BOTTOM
                b_h = [l[2], l[3]-1, l[2], l[3]]
                l_v = [l[0], l[1], l[0]+1, l[1]]
                if b_h in lines and not line_overlaps(l_v, lines):
                    if self.incomplete_exist(l_v, lines, "ver_left"):
                        return l_v, 2
                    return l_v, 1
                elif l_v in lines and\
                        not line_overlaps(b_h, lines):
                    if self.incomplete_exist(b_h, lines, "hor_bot"):
                        return b_h, 2
                    return b_h, 1
Example #2
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