/*
* Copyright (c) 2019-2021, yzrh <yzrh@noema.org>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdbool.h>
#include "extern.h"
#include "screen.h"
static bool
_collision(int *fruit, Snake_Pos **snake)
{
Snake_Pos *ptr = *snake;
while (ptr != NULL) {
if (collision_block(fruit, (int[]) {ptr->x, ptr->y}))
return 1;
ptr = ptr->next;
}
return 0;
}
static bool
_bound(Snake_Pos **snake)
{
if ((*snake)->x + SCREEN_UNIT / 2 > SCREEN_HEIGHT)
return 1;
else if ((*snake)->y + SCREEN_UNIT / 2 > SCREEN_HEIGHT)
return 1;
else if ((*snake)->x - SCREEN_UNIT / 2 < -SCREEN_UNIT)
return 1;
else if ((*snake)->y - SCREEN_UNIT / 2 < -SCREEN_UNIT)
return 1;
return 0;
}
static int
_overlap(Snake_Pos **snake0, Snake_Pos **snake1)
{
Snake_Pos *ptr0 = *snake0;
Snake_Pos *ptr1;
int is_overlap[2] = {0, 0};
while (ptr0->next->next != NULL) {
if (collision_block(
(int[]) {(*snake0)->x, (*snake0)->y},
(int[]) {ptr0->next->x, ptr0->next->y})) {
is_overlap[0] = 1;
break;
}
ptr0 = ptr0->next;
}
if (snake1 != NULL) {
ptr0 = *snake0;
ptr1 = *snake1;
while (ptr0->next->next != NULL &&
ptr1->next->next != NULL &&
(is_overlap[0] != 1 || is_overlap[1] != 2)) {
if (collision_block(
(int[]) {(*snake0)->x, (*snake0)->y},
(int[]) {ptr1->next->x, ptr1->next->y}))
is_overlap[0] = 1;
if (collision_block(
(int[]) {(*snake1)->x, (*snake1)->y},
(int[]) {ptr0->next->x, ptr0->next->y}))
is_overlap[1] = 2;
if (collision_block(
(int[]) {(*snake1)->x, (*snake1)->y},
(int[]) {ptr1->next->x, ptr1->next->y}))
is_overlap[1] = 2;
ptr0 = ptr0->next;
ptr1 = ptr1->next;
}
}
return is_overlap[0] + is_overlap[1];
}
void
game_snake_init(Snake_Pos **snake, int dx)
{
Snake_Pos *ptr;
while ((ptr = *snake) != NULL) {
*snake = (*snake)->next;
free(ptr);
}
*snake = malloc(sizeof(Snake_Pos));
(*snake)->next = malloc(sizeof(Snake_Pos));
(*snake)->x = (*snake)->next->x = SCREEN_HEIGHT / 2 + dx;
(*snake)->y = SCREEN_HEIGHT / 2;
(*snake)->next->y = SCREEN_HEIGHT / 2 + SCREEN_UNIT;
(*snake)->next->next = NULL;
}
void
game_snake_move(Snake_Pos **snake0, Snake_Pos **snake1,
Snake_Input *head, bool law, int *legal)
{
bool bound_snake0;
bool bound_snake1;
int overlap;
Snake_Pos *ptr0;
Snake_Pos *ptr1;
ptr0 = malloc(sizeof(Snake_Pos));
ptr0->x = (*snake0)->x;
ptr0->y = (*snake0)->y;
ptr0->next = *snake0;
switch (head[0]) {
case SNAKE_LEFT:
ptr0->x -= SCREEN_UNIT;
break;
case SNAKE_UP:
ptr0->y -= SCREEN_UNIT;
break;
case SNAKE_DOWN:
ptr0->y += SCREEN_UNIT;
break;
case SNAKE_RIGHT:
ptr0->x += SCREEN_UNIT;
break;
default:
break;
}
bound_snake0 = _bound(&ptr0);
if (snake1 == NULL) {
overlap = _overlap(&ptr0, NULL);
if (bound_snake0 || overlap == 1)
*legal = 1;
else
*legal = 0;
} else {
ptr1 = malloc(sizeof(Snake_Pos));
ptr1->x = (*snake1)->x;
ptr1->y = (*snake1)->y;
ptr1->next = *snake1;
switch (head[1]) {
case SNAKE_LEFT:
ptr1->x -= SCREEN_UNIT;
break;
case SNAKE_UP:
ptr1->y -= SCREEN_UNIT;
break;
case SNAKE_DOWN:
ptr1->y += SCREEN_UNIT;
break;
case SNAKE_RIGHT:
ptr1->x += SCREEN_UNIT;
break;
default:
break;
}
bound_snake1 = _bound(&ptr1);
overlap = _overlap(&ptr0, &ptr1);
if ((bound_snake0 && bound_snake1) || overlap == 3)
*legal = 3;
else if (bound_snake1 || overlap == 2)
*legal = 2;
else if (bound_snake0 || overlap == 1)
*legal = 1;
else
*legal = 0;
}
if (*legal == 0 || !law) {
*snake0 = ptr0;
while (ptr0->next->next != NULL)
ptr0 = ptr0->next;
free(ptr0->next);
ptr0->next = NULL;
if (snake1 != NULL) {
*snake1 = ptr1;
while (ptr1->next->next != NULL)
ptr1 = ptr1->next;
free(ptr1->next);
ptr1->next = NULL;
}
} else {
free(ptr0);
if (snake1 != NULL)
free(ptr1);
}
}
void
game_snake_grow(Snake_Pos **snake)
{
Snake_Pos *ptr = *snake;
while (ptr->next != NULL)
ptr = ptr->next;
ptr->next = malloc(sizeof(Snake_Pos));
ptr->next->x = ptr->x;
ptr->next->y = ptr->y;
ptr->next->next = NULL;
}
void
game_fruit(int *fruit, Snake_Pos **snake)
{
do {
fruit[0] = rand() % SCREEN_SIDE * SCREEN_UNIT;
fruit[1] = rand() % SCREEN_SIDE * SCREEN_UNIT;
} while (_collision(fruit, snake));
}
void
game_snake_fall(Snake_Pos **snake, bool norm, int speed)
{
Snake_Pos *ptr = *snake;
while(ptr != NULL) {
ptr->y += norm ? speed : -speed;
ptr = ptr->next;
}
}
void
game_pipe_init(Snake_Pos **pipe)
{
Snake_Pos *ptr;
while ((ptr = *pipe) != NULL) {
*pipe = (*pipe)->next;
free(ptr);
}
*pipe = malloc(sizeof(Snake_Pos));
(*pipe)->x = SCREEN_HEIGHT;
(*pipe)->y = (rand() % (SCREEN_SIDE - PIPE_GAP) + PIPE_GAP) *
SCREEN_UNIT;
(*pipe)->next = NULL;
}
void
game_pipe_move(Snake_Pos **pipe)
{
Snake_Pos *ptr = *pipe;
while(ptr != NULL) {
ptr->x -= 1;
ptr = ptr->next;
}
}
void
game_pipe_add(Snake_Pos **pipe)
{
Snake_Pos *ptr = *pipe;
while(ptr->next != NULL)
ptr = ptr->next;
ptr->next = malloc(sizeof(Snake_Pos));
ptr->next->x = SCREEN_HEIGHT;
ptr->next->y = (rand() % (SCREEN_SIDE - PIPE_GAP) + PIPE_GAP) *
SCREEN_UNIT;
ptr->next->next = NULL;
}
void
game_pipe_del(Snake_Pos **pipe)
{
Snake_Pos *ptr = *pipe;
*pipe = (*pipe)->next;
free(ptr);
}
void
game_pipe_pos(Snake_Pos **pipe, int *state)
{
Snake_Pos *ptr = *pipe;
*state = 0;
*state += ptr->x + SCREEN_UNIT < 0 ? 1 : 0;
while(ptr->next != NULL)
ptr = ptr->next;
*state += ptr->x < SCREEN_HEIGHT - PIPE_DISTANCE * SCREEN_UNIT ? 2 : 0;
}
/*
* This function returns the dimension of pipes
* that snake is currently passing through,
* so that we can avoid checking all pipes for
* collision
*
* Array elements: x, w, y0, h0, y1, h1
*/
void
game_pipe_bound(Snake_Pos **pipe, int *pos)
{
Snake_Pos *ptr = *pipe;
while(ptr != NULL) {
if (ptr->x < SCREEN_HEIGHT / 2 &&
ptr->x + SCREEN_UNIT > SCREEN_HEIGHT / 2) {
pos[0] = ptr->x;
pos[1] = SCREEN_UNIT;
pos[2] = 0;
pos[3] = ptr->y - PIPE_GAP / 2 * SCREEN_UNIT;
pos[4] = ptr->y + PIPE_GAP / 2 * SCREEN_UNIT;
pos[5] = SCREEN_HEIGHT - ptr->y - SCREEN_UNIT;
return;
}
ptr = ptr->next;
}
pos[0] = 0;
}