Mercurial > projects > rantaiwarna
diff board.c @ 0:a9a7ad180c3b version-1
Initial revision
author | Guido Berhoerster <guido+rantaiwarna@berhoerster.name> |
---|---|
date | Sat, 15 Mar 2014 18:41:03 +0100 |
parents | |
children | aec74ae4d6e5 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/board.c Sat Mar 15 18:41:03 2014 +0100 @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2014 Guido Berhoerster <guido+rantaiwarna@berhoerster.name> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#define _XOPEN_SOURCE 600 + +#include <stdlib.h> +#include <unistd.h> +#include <curses.h> /* for OK, ERR */ + +#include "board.h" +#include "compat.h" +#include "util.h" + +struct stack_element { + struct stack_element *next; + int x; + int y; +}; + +struct board_ctx * +board_create(int height, int width, short colors) +{ + struct board_ctx *board; + + board = rantaiwarna_malloc(sizeof (struct board_ctx)); + board->elements = rantaiwarna_calloc((size_t)(width * height), + sizeof (short)); + board->seed = (uint32_t)getpid(); + board->height = height; + board->width = width; + board->colors = colors; + board->status = GAME_OVER; + board->score = 0; + if (board_generate(board) == ERR) { + board_free(board); + return (NULL); + } + + return (board); +} + +void +board_free(struct board_ctx *board) +{ + free(board->elements); + free(board); +} + +int +board_check_status(struct board_ctx *board) +{ + int status = GAME_OVER | GAME_OVER_CLEARED; + int y; + int x; + + for (y = board->height - 1; y >= 0; y--) { + for (x = 0; x < board->width; x++) { + if (board->elements[y * board->width + x] != 0) { + status &= ~GAME_OVER_CLEARED; + if (((y - 1 >= 0) && + (board->elements[y * board->width + x] == + board->elements[(y - 1) * board->width + + x])) || + ((x + 1 < board->width) && + (board->elements[y * board->width + x] == + (board->elements[y * board->width + x + + 1])))) { + status &= ~GAME_OVER; + goto out; + } + } + } + } + +out: + board->status = status; + return (status); +} + +int +board_generate(struct board_ctx *board) +{ + int i; + int j; + + /* make up to 1000 attempts to create a board that is playable */ + for (i = 0; i < 1000; i++) { + for (j = 0; j < board->width * board->height; j++) { + board->elements[j] = + (short)(rantaiwarna_rand(&board->seed) % + board->colors) + 1; + } + if (board_check_status(board) & GAME_OVER) { + return (ERR); + } + } + + board->score = 0; + + return (OK); +} + +int +board_compact(struct board_ctx *board) +{ + int changes = 0; + int x; + int y; + int top; + int left; + + /* close vertical gaps iby moving elements down */ + for (x = 0; x < board->width; x++) { + for (y = top = board->height - 1; y >= 0; y--) { + if (board->elements[y * board->width + x] != 0) { + board->elements[top * board->width + x] = + board->elements[y * board->width + x]; + top--; + changes++; + } + } + for (y = top; y >= 0; y--) { + board->elements[y * board->width + x] = 0; + } + } + + /* close column gaps by moving columns to the left */ + for (x = left = 0; x < board->width; x++) { + if (board->elements[(board->height - 1) * board->width + x] != + 0) { + if (x > left) { + for (y = 0; y < board->height; y++) { + board->elements[y * board->width + + left] = board->elements[y * + board->width + x]; + } + changes++; + } + left++; + } + } + for (x = left; x < board->width; x++) { + for (y = 0; y < board->height; y++) { + board->elements[y * board->width + x] = 0; + } + } + + return (changes); +} + +int +board_remove_elements(struct board_ctx *board, int start_y, int start_x) +{ + int removed = 0; + int color; + int x; + int y; + int west; + int east; + int n = 0; + int i; + int save_x; + int save_y; + struct stack_element *stack_start; + struct stack_element *stack_tmp; + struct stack_element *stack_new; + + color = board->elements[start_y * board->width + start_x]; + if (color == 0) { + return (0); + } + + stack_new = rantaiwarna_malloc(sizeof (struct stack_element)); + stack_new->next = NULL; + stack_new->y = start_y; + stack_new->x = start_x; + stack_start = stack_new; + + while (stack_start != NULL) { + x = stack_start->x; + y = stack_start->y; + stack_tmp = stack_start; + stack_start = stack_start->next; + free(stack_tmp); + + if (board->elements[y * board->width + x] != color) { + continue; + } + + for (west = x; (west - 1 >= 0) && + (board->elements[y * board->width + west - 1] == color); + west--); + for (east = x; (east + 1 < board->width) && + (board->elements[y * board->width + east + 1] == color); + east++); + for (i = west; i <= east; i++) { + /* + * only really start removing elements if there are + * more than two adjacent elements of the same color + */ + n++; + if (n < 2) { + save_x = i; + save_y = y; + } else { + if (n == 2) { + board->elements[save_y * board->width + + save_x] = 0; + removed++; + } + board->elements[y * board->width + i] = 0; + removed++; + } + + if ((y - 1 >= 0) && + (board->elements[(y - 1) * board->width + i] == + color)) { + stack_new = rantaiwarna_malloc( + sizeof (struct stack_element)); + stack_new->next = stack_start; + stack_new->y = y - 1; + stack_new->x = i; + stack_start = stack_new; + } + if ((y + 1 < board->height) && + (board->elements[(y + 1) * board->width + i] == + color)) { + stack_new = rantaiwarna_malloc( + sizeof (struct stack_element)); + stack_new->next = stack_start; + stack_new->y = y + 1; + stack_new->x = i; + stack_start = stack_new; + } + } + } + if (removed > 0) { + board_compact(board); + board->score += (removed - 1) * (removed - 1); + board_check_status(board); + if (board->status & GAME_OVER) { + if (board->status & GAME_OVER_CLEARED) { + board->score += 1000; + } + } + } + + return (removed); +}