import random
import cvzone
import math
import cv2
import numpy as np
from cvzone.HandTrackingModule import HandDetector

Logic of the game

  • First, we detect the index finger point of the hand using CVzone package and extract it.
  • Then we initialize the list to store all points, list to store all distances between points, create variable to store current length, variable to store allowed distance which is 150 in our case and we set a previousHead variable as 0,0 for previous head position.
  • Now we create a SarpaGame Class for creating a game where we have initialized above mentioned list and variables.
  • Then we create a update method where we write all the logic of the game. Lets learn step by step :

    • First we check if the game is over or not. If not then we initialized the previous head points and current head points and we calculate the distance between them. and append the result in length list.
    • Then we update the update the current length position and previous head position.

      snake1

    • Now we have to reduce the length since it keep increasing infinitely so which is not desirable.So we check the condition whether our snake length is in range of desired length or not.If not then we reduce the length of the snake by popping out certain number of elements from the list.

      snake2

    • We will draw the snake if there is points by giving input to the cv2.line function as a previous points and current points.

    • Now we will draw a food in the screen by overlaying donut image on the screen by using cvzone package.

    • Now checking if the food is eaten by snake. For that we will create function to generate random food position using randomFoodLocation methods which will give us a random position. Now we will create a logic that if our index is at a correct position we declare it as a eaten.

    • Finally we will check whether their is collision or not. Here we will give the points to our cv2.polylines function to draw the snake and calculate the minimum distance using cv2.pointPolygonTest function. After this we will check if the distance is in our range if not print hit.

cap = cv2.VideoCapture(0)
cap.set(3, 1280)
cap.set(4, 720)
 
detector = HandDetector(detectionCon=0.8, maxHands=1)
 
 
class SarpaGame:
    def __init__(self, pathFood):
        self.points = []  # all points of the snake
        self.lengths = []  # distance between each point
        self.currentLength = 0  # total length of the snake
        self.allowedLength = 150  # total allowed Length
        self.previousHead = 0, 0  # previous head point
 
        self.imgFood = cv2.imread(pathFood, cv2.IMREAD_UNCHANGED)
        self.hFood, self.wFood, _ = self.imgFood.shape
        self.foodPoint = 0, 0
        self.randomFoodLocation()
 
        self.score = 0
        self.gameOver = False
 
    def randomFoodLocation(self):
        self.foodPoint = random.randint(100, 1000), random.randint(100, 600)
 
    def update(self, imgMain, currentHead):
 
        if self.gameOver:
            cvzone.putTextRect(imgMain, "Game Over", [300, 400],
                               scale=7, thickness=5, offset=20)
            cvzone.putTextRect(imgMain, f'Your Score: {self.score}', [300, 550],
                               scale=7, thickness=5, offset=20)
        else:
            px, py = self.previousHead
            cx, cy = currentHead
 
            self.points.append([cx, cy])
            distance = math.hypot(cx - px, cy - py)
            self.lengths.append(distance)
            self.currentLength += distance
            self.previousHead = cx, cy
 
            # Length Reduction
            if self.currentLength > self.allowedLength:
                for i, length in enumerate(self.lengths):
                    self.currentLength -= length
                    self.lengths.pop(i)
                    self.points.pop(i)
                    if self.currentLength < self.allowedLength:
                        break
 
            # Check if snake ate the Food
            rx, ry = self.foodPoint
            if rx - self.wFood // 2 < cx < rx + self.wFood // 2 and \
                    ry - self.hFood // 2 < cy < ry + self.hFood // 2:
                self.randomFoodLocation()
                self.allowedLength += 50
                self.score += 1
                print(self.score)
 
            # Draw Snake
            if self.points:
                for i, point in enumerate(self.points):
                    if i != 0:
                        cv2.line(imgMain, self.points[i - 1], self.points[i], (0, 0, 255), 20)
                cv2.circle(imgMain, self.points[-1], 20, (0, 255, 0), cv2.FILLED)
 
            # Draw Food
            imgMain = cvzone.overlayPNG(imgMain, self.imgFood,
                                        (rx - self.wFood // 2, ry - self.hFood // 2))
 
            cvzone.putTextRect(imgMain, f'Score: {self.score}', [50, 80],
                               scale=3, thickness=3, offset=10)
 
            # Check for Collision
            pts = np.array(self.points[:-2], np.int32)
            pts = pts.reshape((-1, 1, 2))
            cv2.polylines(imgMain, [pts], False, (0, 255, 0), 3)
            minDist = cv2.pointPolygonTest(pts, (cx, cy), True)
 
            if -1 <= minDist <= 1:
                print("Hit")
                self.gameOver = True
                self.points = []  # all points of the snake
                self.lengths = []  # distance between each point
                self.currentLength = 0  # total length of the snake
                self.allowedLength = 150  # total allowed Length
                self.previousHead = 0, 0  # previous head point
                self.randomFoodLocation()
 
        return imgMain
    
game = SarpaGame("./Donut.png")
 
while True:
    success, img = cap.read()
    img = cv2.flip(img, 1)
    hands, img = detector.findHands(img, flipType=False)
 
    if hands:
        lmList = hands[0]['lmList']
        pointIndex = lmList[8][0:2]
        img = game.update(img, pointIndex)
    cv2.imshow("Image", img)
    key = cv2.waitKey(1)
    if key == ord('r'):
        game.gameOver = False
Hit
Hit
Hit
Hit
1
2
3
4
5
Hit