summaryrefslogtreecommitdiff
path: root/snake.py
blob: bec24d0d45219a3e637607b7d5fc9b764565160c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
#!/usr/bin/env python
import sys, pygame, random


class Snake:
    """
    Programmable Game of Snake written in PyGame
    """

    def __init__(self, startat=(0,0), margin=80,length=4,grid_width=30,grid_height=30, grid_pts=30,fps=180):
        # Init attributes
        self.grid_width=grid_width
        self.grid_height=grid_height
        self.grid_pts=grid_pts
        self.margin=margin
        self.default_length=length
        self.attempt=0
        self.fps=fps
        self.startat=startat
        # Setup pygame
        pygame.init()
        self.font=pygame.font.SysFont(pygame.font.get_default_font(), int(self.margin/2))
        self.font_small=pygame.font.SysFont(pygame.font.get_default_font(), int(self.margin/2.5))
        self.screen=pygame.display.set_mode((grid_width*grid_pts,grid_height*grid_pts+margin))

    def new_game(self):
        """
        Reset game state
        """
        self.snake=[self.startat]*self.default_length
        self.direction=3 # Like clock (12=up, 3=right, 6=bottom, 9=left)
        self.new_apple()
        self.score=0
        self.attempt+=1

    def draw_pts(self,x,y,color=(255,255,255),scale=1):
        """
        Draw element on the snake area
        """
        delta=int(self.grid_pts-int(self.grid_pts*scale))
        rect=pygame.Rect(self.grid_pts*x+delta, self.grid_pts*y+self.margin+delta, self.grid_pts-2*delta, self.grid_pts-2*delta)
        pygame.draw.rect(self.screen,color,rect, 0)

    def draw_infos(self,color=(255,255,255),thickness=10):
        """
        Draw the information bar
        """
        # Draw separator
        rect=pygame.Rect(0, self.margin-thickness, self.grid_width*self.grid_pts, thickness)
        pygame.draw.rect(self.screen,(180,180,180),rect, 0)
        # Draw score
        text = self.font.render('Score: '+str(self.score)+"       Length: "+str(len(self.snake))+'       Attempt: '+str(self.attempt), True, color)
        text_center=text.get_rect(center = (self.grid_width*self.grid_pts // 2, (self.margin-thickness) // 2))
        self.screen.blit(text, text_center)
    
    def new_apple(self):
        """
        Create a new apple
        """
        self.apple=(random.randint(0,self.grid_width-1),random.randint(0,self.grid_height-1))
        if self.apple in self.snake:
            self.new_apple()

    def move(self):
        """
        Move the snake
        """
        # Update tail
        if len(self.snake)>1:
            tmp=self.snake[0]
            for i in range(1,len(self.snake)):
                newtmp=self.snake[i]
                self.snake[i]=tmp
                tmp=newtmp
        # Update head
        h=self.snake[0] # Head
        if self.direction==3:
            self.snake[0]=(h[0]+1,h[1])
        elif self.direction==9:
            self.snake[0]=(h[0]-1,h[1])
        elif self.direction==12:
            self.snake[0]=(h[0],h[1]-1)
        else:
            self.snake[0]=(h[0],h[1]+1)

    def draw_snake(self):
        """
        Draw the snake with color effects
        """
        for i in range(0,len(self.snake)):
            color=(0,150,150) if i==0 else (0,max(255-i*10,120),0) 
            elt=self.snake[i]
            self.draw_pts(elt[0],elt[1],color=color)
            if i>0:
                self.draw_pts(elt[0],elt[1],color=(color[0],int(color[1]/2),color[2]),scale=max(0.7,1-i/4))
            else:
                self.draw_pts(elt[0],elt[1],color=(color[0],int(color[1]/2),color[2]),scale=0.86)

    def draw_apple(self,radius=None):
        """ 
        Draw the apple on the screen
        """
        if radius==None:
            radius=self.grid_pts/2
        pygame.draw.circle(self.screen, (210,0,0), (self.apple[0]*self.grid_pts+int(self.grid_pts/2),self.apple[1]*self.grid_pts+self.margin+int(self.grid_pts/2)), radius)
        pygame.draw.circle(self.screen, (170,0,0), (self.apple[0]*self.grid_pts+int(self.grid_pts/2),self.apple[1]*self.grid_pts+self.margin+int(self.grid_pts/2)), radius/1.5)


    def has_loose(self):
        """
        Return true if the game is loose
        """
        if self.snake.count(self.snake[0])>1:
            return(True)
        h=self.snake[0]
        if h[0]<0 or h[1]<0 or h[0] >= self.grid_width or h[1] >= self.grid_height:
            return(True)
        return(False)


    def run(self, event_handler=None):
        """
        Play a game (one attempt)
        """
        clock = pygame.time.Clock()
        ignore_has_loose=True
        self.new_game()
        last_event=0 # 0 is nothing, 1 is eat an apple and -1 loose
        while True:
            self.screen.fill((0,0,0))
            self.draw_snake()
            self.draw_apple()
            self.draw_infos()
            # Check for loose
            if not(ignore_has_loose) and self.has_loose():
                event_handler(self,-1)
                break
            else:
                ignore_has_loose=False
            # Check inputs
            for event in pygame.event.get():
                if event.type == pygame.QUIT: 
                    pygame.quit()
                    sys.exit()
                elif event_handler==None and event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_LEFT and self.direction != 3:
                        self.direction=9
                        break
                    elif event.key == pygame.K_RIGHT and self.direction != 9:
                        self.direction=3
                        break
                    elif event.key == pygame.K_UP and self.direction != 6:
                        self.direction=12
                        break
                    elif event.key == pygame.K_DOWN and self.direction != 12:
                        self.direction=6
                        break
            # Check if an event handler is available
            if event_handler!=None:
                code=event_handler(self,last_event)
                if code < 0:
                    break
                last_event=0
            self.move()
            # Check for eating apple
            if self.apple==self.snake[0]:
                self.snake.append(self.snake[len(self.snake)-1])
                self.new_apple()
                self.score+=1
                last_event=1
            pygame.display.flip()
            clock.tick(self.fps)
        return(self.score)