SDL

Форк
0
154 строки · 4.6 Кб
1
/*
2
 * Logic implementation of the Snake game. It is designed to efficiently
3
 * represent in memory the state of the game.
4
 *
5
 * This code is public domain. Feel free to use it for any purpose!
6
 */
7
#include "snake.h"
8

9
#include <limits.h> /* CHAR_BIT, CHAR_MAX */
10
#include <string.h> /* memcpy() */
11

12
#define THREE_BITS  0x7U /* ~CHAR_MAX >> (CHAR_BIT - SNAKE_CELL_MAX_BITS) */
13
#define SHIFT(x, y) (((x) + ((y) * SNAKE_GAME_WIDTH)) * SNAKE_CELL_MAX_BITS)
14

15
static void put_cell_at_(SnakeContext *ctx, char x, char y, SnakeCell ct)
16
{
17
    const int shift = SHIFT(x, y);
18
    const int adjust = shift % CHAR_BIT;
19
    unsigned char *const pos = ctx->cells + (shift / CHAR_BIT);
20
    unsigned short range;
21
    memcpy(&range, pos, sizeof(range));
22
    range &= ~(THREE_BITS << adjust); /* clear bits */
23
    range |= (ct & THREE_BITS) << adjust;
24
    memcpy(pos, &range, sizeof(range));
25
}
26

27
static int are_cells_full_(SnakeContext *ctx)
28
{
29
    return ctx->occupied_cells == SNAKE_GAME_WIDTH * SNAKE_GAME_HEIGHT;
30
}
31

32
static void new_food_pos_(SnakeContext *ctx, RandFunc rand)
33
{
34
    char x;
35
    char y;
36
    for (;;) {
37
        x = (char) rand(SNAKE_GAME_WIDTH);
38
        y = (char) rand(SNAKE_GAME_HEIGHT);
39
        if (snake_cell_at(ctx, x, y) == SNAKE_CELL_NOTHING) {
40
            put_cell_at_(ctx, x, y, SNAKE_CELL_FOOD);
41
            break;
42
        }
43
    }
44
}
45

46
void snake_initialize(SnakeContext *ctx, RandFunc rand)
47
{
48
    int i;
49
    memset(ctx, 0, sizeof ctx->cells);
50
    ctx->head_xpos = ctx->tail_xpos = SNAKE_GAME_WIDTH / 2;
51
    ctx->head_ypos = ctx->tail_ypos = SNAKE_GAME_HEIGHT / 2;
52
    ctx->next_dir = SNAKE_DIR_RIGHT;
53
    ctx->inhibit_tail_step = ctx->occupied_cells = 4;
54
    --ctx->occupied_cells;
55
    put_cell_at_(ctx, ctx->tail_xpos, ctx->tail_ypos, SNAKE_CELL_SRIGHT);
56
    for (i = 0; i < 4; i++) {
57
        new_food_pos_(ctx, rand);
58
        ++ctx->occupied_cells;
59
    }
60
}
61

62
void snake_redir(SnakeContext *ctx, SnakeDirection dir)
63
{
64
    SnakeCell ct = snake_cell_at(ctx, ctx->head_xpos, ctx->head_ypos);
65
    if ((dir == SNAKE_DIR_RIGHT && ct != SNAKE_CELL_SLEFT) ||
66
        (dir == SNAKE_DIR_UP && ct != SNAKE_CELL_SDOWN) ||
67
        (dir == SNAKE_DIR_LEFT && ct != SNAKE_CELL_SRIGHT) ||
68
        (dir == SNAKE_DIR_DOWN && ct != SNAKE_CELL_SUP))
69
        ctx->next_dir = dir;
70
}
71

72
static void wrap_around_(char *val, char max)
73
{
74
    if (*val < 0)
75
        *val = max - 1;
76
    if (*val > max - 1)
77
        *val = 0;
78
}
79

80
void snake_step(SnakeContext *ctx, RandFunc rand)
81
{
82
    const SnakeCell dir_as_cell = (SnakeCell)(ctx->next_dir + 1);
83
    SnakeCell ct;
84
    char prev_xpos;
85
    char prev_ypos;
86
    /* Move tail forward */
87
    if (--ctx->inhibit_tail_step == 0) {
88
        ++ctx->inhibit_tail_step;
89
        ct = snake_cell_at(ctx, ctx->tail_xpos, ctx->tail_ypos);
90
        put_cell_at_(ctx, ctx->tail_xpos, ctx->tail_ypos, SNAKE_CELL_NOTHING);
91
        switch (ct) {
92
        case SNAKE_CELL_SRIGHT:
93
            ctx->tail_xpos++;
94
            break;
95
        case SNAKE_CELL_SUP:
96
            ctx->tail_ypos--;
97
            break;
98
        case SNAKE_CELL_SLEFT:
99
            ctx->tail_xpos--;
100
            break;
101
        case SNAKE_CELL_SDOWN:
102
            ctx->tail_ypos++;
103
            break;
104
        default:
105
            break;
106
        }
107
        wrap_around_(&ctx->tail_xpos, SNAKE_GAME_WIDTH);
108
        wrap_around_(&ctx->tail_ypos, SNAKE_GAME_HEIGHT);
109
    }
110
    /* Move head forward */
111
    prev_xpos = ctx->head_xpos;
112
    prev_ypos = ctx->head_ypos;
113
    switch (ctx->next_dir) {
114
    case SNAKE_DIR_RIGHT:
115
        ++ctx->head_xpos;
116
        break;
117
    case SNAKE_DIR_UP:
118
        --ctx->head_ypos;
119
        break;
120
    case SNAKE_DIR_LEFT:
121
        --ctx->head_xpos;
122
        break;
123
    case SNAKE_DIR_DOWN:
124
        ++ctx->head_ypos;
125
        break;
126
    }
127
    wrap_around_(&ctx->head_xpos, SNAKE_GAME_WIDTH);
128
    wrap_around_(&ctx->head_ypos, SNAKE_GAME_HEIGHT);
129
    /* Collisions */
130
    ct = snake_cell_at(ctx, ctx->head_xpos, ctx->head_ypos);
131
    if (ct != SNAKE_CELL_NOTHING && ct != SNAKE_CELL_FOOD) {
132
        snake_initialize(ctx, rand);
133
        return;
134
    }
135
    put_cell_at_(ctx, prev_xpos, prev_ypos, dir_as_cell);
136
    put_cell_at_(ctx, ctx->head_xpos, ctx->head_ypos, dir_as_cell);
137
    if (ct == SNAKE_CELL_FOOD) {
138
        if (are_cells_full_(ctx)) {
139
            snake_initialize(ctx, rand);
140
            return;
141
        }
142
        new_food_pos_(ctx, rand);
143
        ++ctx->inhibit_tail_step;
144
        ++ctx->occupied_cells;
145
    }
146
}
147

148
SnakeCell snake_cell_at(const SnakeContext *ctx, char x, char y)
149
{
150
    const int shift = SHIFT(x, y);
151
    unsigned short range;
152
    memcpy(&range, ctx->cells + (shift / CHAR_BIT), sizeof(range));
153
    return (SnakeCell)((range >> (shift % CHAR_BIT)) & THREE_BITS);
154
}
155

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.