diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 38396562e..f010ac282 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -14,7 +14,11 @@ ******************************************************************************/ #include "gamescc.h" -#include "tetris.c" // replace with game code +#ifdef BUILD_PRICES +#include "prices.c" +#elif +#include "tetris.c" +#endif int32_t GAMEDATA(struct games_player *P,void *ptr); @@ -161,8 +165,11 @@ int32_t games_replay2(uint8_t *newdata,uint64_t seed,gamesevent *keystrokes,int3 } #ifndef STANDALONE - -#include "tetris.cpp" // replace with game specific functions +#ifdef BUILD_PRICES +#include "prices.cpp" +#elif +#include "tetris.cpp" +#endif void GAMEJSON(UniValue &obj,struct games_player *P); diff --git a/src/cc/makecclib b/src/cc/makecclib index 35b4a9d8d..38634aa78 100755 --- a/src/cc/makecclib +++ b/src/cc/makecclib @@ -12,6 +12,10 @@ echo games tetris gcc -O3 -DBUILD_GAMESCC -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c -o gamescc.so cclib.cpp ./maketetris +echo games prices +gcc -O3 -DBUILD_GAMESCC -DBUILD_PRICES -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c -o pricescc.so cclib.cpp +./makeprices + echo customcc stub gcc -O3 -DBUILD_CUSTOMCC -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c -o customcc.so cclib.cpp diff --git a/src/cc/prices.c b/src/cc/prices.c new file mode 100644 index 000000000..651b1018c --- /dev/null +++ b/src/cc/prices.c @@ -0,0 +1,896 @@ + +#include "tetris.h" + +/* + In order to port a game into gamesCC, the RNG needs to be seeded with the gametxid seed, also events needs to be broadcast using issue_games_events. Also the game engine needs to be daemonized, preferably by putting all globals into a single data structure. + + also, the standalone game needs to support argv of seed gametxid, along with replay args + */ + +int random_tetromino(struct games_state *rs) +{ + rs->seed = _games_rngnext(rs->seed); + return(rs->seed % NUM_TETROMINOS); +} + +int32_t tetrisdata(struct games_player *P,void *ptr) +{ + tetris_game *tg = (tetris_game *)ptr; + P->gold = tg->points; + P->dungeonlevel = tg->level; + //fprintf(stderr,"score.%d level.%d\n",tg->points,tg->level); + return(0); +} + +/***************************************************************************/ +/** https://github.com/brenns10/tetris + @file main.c + @author Stephen Brennan + @date Created Wednesday, 10 June 2015 + @brief Main program for tetris. + @copyright Copyright (c) 2015, Stephen Brennan. Released under the Revised + BSD License. See LICENSE.txt for details. + *******************************************************************************/ + + +#include // for FILE +#include // for bool +#include +#include +#include +#include +#include +#include + +#ifdef BUILD_GAMESCC +#include "rogue/cursesd.h" +#else +#include +#endif + + +#define MAX(X,Y) ((X) > (Y) ? (X) : (Y)) +#define MIN(X,Y) ((X) < (Y) ? (X) : (Y)) + +/******************************************************************************* + Array Definitions + *******************************************************************************/ + +const tetris_location TETROMINOS[NUM_TETROMINOS][NUM_ORIENTATIONS][TETRIS] = +{ + // I + {{{1, 0}, {1, 1}, {1, 2}, {1, 3}}, + {{0, 2}, {1, 2}, {2, 2}, {3, 2}}, + {{3, 0}, {3, 1}, {3, 2}, {3, 3}}, + {{0, 1}, {1, 1}, {2, 1}, {3, 1}}}, + // J + {{{0, 0}, {1, 0}, {1, 1}, {1, 2}}, + {{0, 1}, {0, 2}, {1, 1}, {2, 1}}, + {{1, 0}, {1, 1}, {1, 2}, {2, 2}}, + {{0, 1}, {1, 1}, {2, 0}, {2, 1}}}, + // L + {{{0, 2}, {1, 0}, {1, 1}, {1, 2}}, + {{0, 1}, {1, 1}, {2, 1}, {2, 2}}, + {{1, 0}, {1, 1}, {1, 2}, {2, 0}}, + {{0, 0}, {0, 1}, {1, 1}, {2, 1}}}, + // O + {{{0, 1}, {0, 2}, {1, 1}, {1, 2}}, + {{0, 1}, {0, 2}, {1, 1}, {1, 2}}, + {{0, 1}, {0, 2}, {1, 1}, {1, 2}}, + {{0, 1}, {0, 2}, {1, 1}, {1, 2}}}, + // S + {{{0, 1}, {0, 2}, {1, 0}, {1, 1}}, + {{0, 1}, {1, 1}, {1, 2}, {2, 2}}, + {{1, 1}, {1, 2}, {2, 0}, {2, 1}}, + {{0, 0}, {1, 0}, {1, 1}, {2, 1}}}, + // T + {{{0, 1}, {1, 0}, {1, 1}, {1, 2}}, + {{0, 1}, {1, 1}, {1, 2}, {2, 1}}, + {{1, 0}, {1, 1}, {1, 2}, {2, 1}}, + {{0, 1}, {1, 0}, {1, 1}, {2, 1}}}, + // Z + {{{0, 0}, {0, 1}, {1, 1}, {1, 2}}, + {{0, 2}, {1, 1}, {1, 2}, {2, 1}}, + {{1, 0}, {1, 1}, {2, 1}, {2, 2}}, + {{0, 1}, {1, 0}, {1, 1}, {2, 0}}}, +}; + +const int GRAVITY_LEVEL[MAX_LEVEL+1] = { + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 50, 48, 46, 44, 42, 40, 38, 36, 34, 32, + //10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 30, 28, 26, 24, 22, 20, 16, 12, 8, 4 +}; + +/******************************************************************************* + Helper Functions for Blocks + *******************************************************************************/ + +void sleep_milli(int milliseconds) +{ + struct timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = milliseconds * 1000 * 1000; + nanosleep(&ts, NULL); +} + +/* + Return the block at the given row and column. + */ +char tg_get(tetris_game *obj, int row, int column) +{ + return obj->board[obj->cols * row + column]; +} + +/* + Set the block at the given row and column. + */ +static void tg_set(tetris_game *obj, int row, int column, char value) +{ + obj->board[obj->cols * row + column] = value; +} + +/* + Check whether a row and column are in bounds. + */ +bool tg_check(tetris_game *obj, int row, int col) +{ + return 0 <= row && row < obj->rows && 0 <= col && col < obj->cols; +} + +/* + Place a block onto the board. + */ +static void tg_put(tetris_game *obj, tetris_block block) +{ + int i; + for (i = 0; i < TETRIS; i++) { + tetris_location cell = TETROMINOS[block.typ][block.ori][i]; + tg_set(obj, block.loc.row + cell.row, block.loc.col + cell.col, + TYPE_TO_CELL(block.typ)); + } +} + +/* + Clear a block out of the board. + */ +static void tg_remove(tetris_game *obj, tetris_block block) +{ + int i; + for (i = 0; i < TETRIS; i++) { + tetris_location cell = TETROMINOS[block.typ][block.ori][i]; + tg_set(obj, block.loc.row + cell.row, block.loc.col + cell.col, TC_EMPTY); + } +} + +/* + Check if a block can be placed on the board. + */ +static bool tg_fits(tetris_game *obj, tetris_block block) +{ + int i, r, c; + for (i = 0; i < TETRIS; i++) { + tetris_location cell = TETROMINOS[block.typ][block.ori][i]; + r = block.loc.row + cell.row; + c = block.loc.col + cell.col; + if (!tg_check(obj, r, c) || TC_IS_FILLED(tg_get(obj, r, c))) { + return false; + } + } + return true; +} + +/* + Create a new falling block and populate the next falling block with a random + one. + */ +static void tg_new_falling(struct games_state *rs,tetris_game *obj) +{ + // Put in a new falling tetromino. + obj->falling = obj->next; + obj->next.typ = random_tetromino(rs); + obj->next.ori = 0; + obj->next.loc.row = 0; + obj->next.loc.col = obj->cols/2 - 2; +} + +/******************************************************************************* + Game Turn Helpers + *******************************************************************************/ + +/* + Tick gravity, and move the block down if gravity should act. + */ +static void tg_do_gravity_tick(struct games_state *rs,tetris_game *obj) +{ + obj->ticks_till_gravity--; + if (obj->ticks_till_gravity <= 0) { + tg_remove(obj, obj->falling); + obj->falling.loc.row++; + if (tg_fits(obj, obj->falling)) { + obj->ticks_till_gravity = GRAVITY_LEVEL[obj->level]; + } else { + obj->falling.loc.row--; + tg_put(obj, obj->falling); + + tg_new_falling(rs,obj); + } + tg_put(obj, obj->falling); + } +} + +/* + Move the falling tetris block left (-1) or right (+1). + */ +static void tg_move(tetris_game *obj, int direction) +{ + tg_remove(obj, obj->falling); + obj->falling.loc.col += direction; + if (!tg_fits(obj, obj->falling)) { + obj->falling.loc.col -= direction; + } + tg_put(obj, obj->falling); +} + +/* + Send the falling tetris block to the bottom. + */ +static void tg_down(struct games_state *rs,tetris_game *obj) +{ + tg_remove(obj, obj->falling); + while (tg_fits(obj, obj->falling)) { + obj->falling.loc.row++; + } + obj->falling.loc.row--; + tg_put(obj, obj->falling); + tg_new_falling(rs,obj); +} + +/* + Rotate the falling block in either direction (+/-1). + */ +static void tg_rotate(tetris_game *obj, int direction) +{ + tg_remove(obj, obj->falling); + + while (true) { + obj->falling.ori = (obj->falling.ori + direction) % NUM_ORIENTATIONS; + + // If the new orientation fits, we're done. + if (tg_fits(obj, obj->falling)) + break; + + // Otherwise, try moving left to make it fit. + obj->falling.loc.col--; + if (tg_fits(obj, obj->falling)) + break; + + // Finally, try moving right to make it fit. + obj->falling.loc.col += 2; + if (tg_fits(obj, obj->falling)) + break; + + // Put it back in its original location and try the next orientation. + obj->falling.loc.col--; + // Worst case, we come back to the original orientation and it fits, so this + // loop will terminate. + } + + tg_put(obj, obj->falling); +} + +/* + Swap the falling block with the block in the hold buffer. + */ +static void tg_hold(struct games_state *rs,tetris_game *obj) +{ + tg_remove(obj, obj->falling); + if (obj->stored.typ == -1) { + obj->stored = obj->falling; + tg_new_falling(rs,obj); + } else { + int typ = obj->falling.typ, ori = obj->falling.ori; + obj->falling.typ = obj->stored.typ; + obj->falling.ori = obj->stored.ori; + obj->stored.typ = typ; + obj->stored.ori = ori; + while (!tg_fits(obj, obj->falling)) { + obj->falling.loc.row--; + } + } + tg_put(obj, obj->falling); +} + +/* + Perform the action specified by the move. + */ +static void tg_handle_move(struct games_state *rs,tetris_game *obj, tetris_move move) +{ + switch (move) { + case TM_LEFT: + //fprintf(stderr,"LEFT "); + tg_move(obj, -1); + break; + case TM_RIGHT: + //fprintf(stderr,"RIGHT "); + tg_move(obj, 1); + break; + case TM_DROP: + tg_down(rs,obj); + break; + case TM_CLOCK: + tg_rotate(obj, 1); + break; + case TM_COUNTER: + tg_rotate(obj, -1); + break; + case TM_HOLD: + tg_hold(rs,obj); + break; + default: + // pass + break; + } +} + +/* + Return true if line i is full. + */ +static bool tg_line_full(tetris_game *obj, int i) +{ + int j; + for (j = 0; j < obj->cols; j++) { + if (TC_IS_EMPTY(tg_get(obj, i, j))) + return false; + } + return true; +} + +/* + Shift every row above r down one. + */ +static void tg_shift_lines(tetris_game *obj, int r) +{ + int i, j; + for (i = r-1; i >= 0; i--) { + for (j = 0; j < obj->cols; j++) { + tg_set(obj, i+1, j, tg_get(obj, i, j)); + tg_set(obj, i, j, TC_EMPTY); + } + } +} + +/* + Find rows that are filled, remove them, shift, and return the number of + cleared rows. + */ +static int tg_check_lines(tetris_game *obj) +{ + int i, nlines = 0; + tg_remove(obj, obj->falling); // don't want to mess up falling block + + for (i = obj->rows-1; i >= 0; i--) { + if (tg_line_full(obj, i)) { + tg_shift_lines(obj, i); + i++; // do this line over again since they're shifted + nlines++; + } + } + + tg_put(obj, obj->falling); // replace + return nlines; +} + +/* + Adjust the score for the game, given how many lines were just cleared. + */ +static void tg_adjust_score(tetris_game *obj, int lines_cleared) +{ + static int line_multiplier[] = {0, 40, 100, 300, 1200}; + obj->points += line_multiplier[lines_cleared] * (obj->level + 1); + if (lines_cleared >= obj->lines_remaining) { + obj->level = MIN(MAX_LEVEL, obj->level + 1); + lines_cleared -= obj->lines_remaining; + obj->lines_remaining = LINES_PER_LEVEL - lines_cleared; + } else { + obj->lines_remaining -= lines_cleared; + } +} + +/* + Return true if the game is over. + */ +static bool tg_game_over(tetris_game *obj) +{ + int i, j; + bool over = false; + tg_remove(obj, obj->falling); + for (i = 0; i < 2; i++) { + for (j = 0; j < obj->cols; j++) { + if (TC_IS_FILLED(tg_get(obj, i, j))) { + over = true; + } + } + } + tg_put(obj, obj->falling); + return over; +} + +/******************************************************************************* + Main Public Functions + *******************************************************************************/ + +/* + Do a single game tick: process gravity, user input, and score. Return true if + the game is still running, false if it is over. + */ +bool tg_tick(struct games_state *rs,tetris_game *obj, tetris_move move) +{ + int lines_cleared; + // Handle gravity. + tg_do_gravity_tick(rs,obj); + + // Handle input. + tg_handle_move(rs,obj, move); + + // Check for cleared lines + lines_cleared = tg_check_lines(obj); + + tg_adjust_score(obj, lines_cleared); + + // Return whether the game will continue (NOT whether it's over) + return !tg_game_over(obj); +} + +void tg_init(struct games_state *rs,tetris_game *obj, int rows, int cols) +{ + // Initialization logic + obj->rows = rows; + obj->cols = cols; + //obj->board = (char *)malloc(rows * cols); + memset(obj->board, TC_EMPTY, rows * cols); + obj->points = 0; + obj->level = 0; + obj->ticks_till_gravity = GRAVITY_LEVEL[obj->level]; + obj->lines_remaining = LINES_PER_LEVEL; + //srand(time(NULL)); + tg_new_falling(rs,obj); + tg_new_falling(rs,obj); + obj->stored.typ = -1; + obj->stored.ori = 0; + obj->stored.loc.row = 0; + obj->next.loc.col = obj->cols/2 - 2; + //printf("%d", obj->falling.loc.col); +} + +tetris_game *tg_create(struct games_state *rs,int rows, int cols) +{ + tetris_game *obj = (tetris_game *)malloc(sizeof(tetris_game) + rows*cols); + tg_init(rs,obj, rows, cols); + return obj; +} + +/*void tg_destroy(tetris_game *obj) +{ + // Cleanup logic + free(obj->board); +}*/ + +void tg_delete(tetris_game *obj) { + //tg_destroy(obj); + free(obj); +} + +/* + Load a game from a file. + +tetris_game *tg_load(FILE *f) +{ + tetris_game *obj = (tetris_game *)malloc(sizeof(tetris_game)); + if (fread(obj, sizeof(tetris_game), 1, f) != 1 ) + { + fprintf(stderr,"read game error\n"); + free(obj); + obj = 0; + } + else + { + obj->board = (char *)malloc(obj->rows * obj->cols); + if (fread(obj->board, sizeof(char), obj->rows * obj->cols, f) != obj->rows * obj->cols ) + { + fprintf(stderr,"fread error\n"); + free(obj->board); + free(obj); + obj = 0; + } + } + return obj; +}*/ + +/* + Save a game to a file. + +void tg_save(tetris_game *obj, FILE *f) +{ + if (fwrite(obj, sizeof(tetris_game), 1, f) != 1 ) + fprintf(stderr,"error writing tetrisgame\n"); + else if (fwrite(obj->board, sizeof(char), obj->rows * obj->cols, f) != obj->rows * obj->cols ) + fprintf(stderr,"error writing board\n"); +}*/ + +/* + Print a game board to a file. Really just for early debugging. + */ +void tg_print(tetris_game *obj, FILE *f) { + int i, j; + for (i = 0; i < obj->rows; i++) { + for (j = 0; j < obj->cols; j++) { + if (TC_IS_EMPTY(tg_get(obj, i, j))) { + fputs(TC_EMPTY_STR, f); + } else { + fputs(TC_BLOCK_STR, f); + } + } + fputc('\n', f); + } +} + +/* + 2 columns per cell makes the game much nicer. + */ +#define COLS_PER_CELL 2 +/* + Macro to print a cell of a specific type to a window. + */ +#define ADD_BLOCK(w,x) waddch((w),' '|A_REVERSE|COLOR_PAIR(x)); \ +waddch((w),' '|A_REVERSE|COLOR_PAIR(x)) +#define ADD_EMPTY(w) waddch((w), ' '); waddch((w), ' ') + +/* + Print the tetris board onto the ncurses window. + */ +void display_board(WINDOW *w, tetris_game *obj) +{ + int i, j; + box(w, 0, 0); + for (i = 0; i < obj->rows; i++) { + wmove(w, 1 + i, 1); + for (j = 0; j < obj->cols; j++) { + if (TC_IS_FILLED(tg_get(obj, i, j))) { + ADD_BLOCK(w,tg_get(obj, i, j)); + } else { + ADD_EMPTY(w); + } + } + } + wnoutrefresh(w); +} + +/* + Display a tetris piece in a dedicated window. + */ +void display_piece(WINDOW *w, tetris_block block) +{ + int b; + tetris_location c; + wclear(w); + box(w, 0, 0); + if (block.typ == -1) { + wnoutrefresh(w); + return; + } + for (b = 0; b < TETRIS; b++) { + c = TETROMINOS[block.typ][block.ori][b]; + wmove(w, c.row + 1, c.col * COLS_PER_CELL + 1); + ADD_BLOCK(w, TYPE_TO_CELL(block.typ)); + } + wnoutrefresh(w); +} + +/* + Display score information in a dedicated window. + */ +void display_score(WINDOW *w, tetris_game *tg) +{ + wclear(w); + box(w, 0, 0); + wprintw(w, (char *)"Score\n%d\n", tg->points); + wprintw(w, (char *)"Level\n%d\n", tg->level); + wprintw(w, (char *)"Lines\n%d\n", tg->lines_remaining); + wnoutrefresh(w); +} + +/* + Save and exit the game. + +void save(tetris_game *game, WINDOW *w) +{ + FILE *f; + + wclear(w); + box(w, 0, 0); // return the border + wmove(w, 1, 1); + wprintw(w, (char *)"Save and exit? [Y/n] "); + wrefresh(w); + timeout(-1); + if (getch() == 'n') { + timeout(0); + return; + } + f = fopen("tetris.save", "w"); + tg_save(game, f); + fclose(f); + tg_delete(game); + endwin(); + fprintf(stderr,"Game saved to \"tetris.save\".\n"); + fprintf(stderr,"Resume by passing the filename as an argument to this program.\n"); + exit(EXIT_SUCCESS); +}*/ + +/* + Do the NCURSES initialization steps for color blocks. + */ +void init_colors(void) +{ + start_color(); + //init_color(COLOR_ORANGE, 1000, 647, 0); + init_pair(TC_CELLI, COLOR_CYAN, COLOR_BLACK); + init_pair(TC_CELLJ, COLOR_BLUE, COLOR_BLACK); + init_pair(TC_CELLL, COLOR_WHITE, COLOR_BLACK); + init_pair(TC_CELLO, COLOR_YELLOW, COLOR_BLACK); + init_pair(TC_CELLS, COLOR_GREEN, COLOR_BLACK); + init_pair(TC_CELLT, COLOR_MAGENTA, COLOR_BLACK); + init_pair(TC_CELLZ, COLOR_RED, COLOR_BLACK); +} + +struct games_state globalR; +extern char Gametxidstr[]; +int32_t issue_games_events(struct games_state *rs,char *gametxidstr,uint32_t eventid,gamesevent c); +gamesevent games_readevent(struct games_state *rs); + +void *gamesiterate(struct games_state *rs) +{ + uint32_t counter = 0; bool running = true; tetris_move move = TM_NONE; + gamesevent c; uint16_t skipcount=0; int32_t prevlevel; uint32_t eventid = 0; tetris_game *tg; + WINDOW *board, *next, *hold, *score; + if ( rs->guiflag != 0 || rs->sleeptime != 0 ) + { + // NCURSES initialization: + initscr(); // initialize curses + cbreak(); // pass key presses to program, but not signals + noecho(); // don't echo key presses to screen + keypad(stdscr, TRUE); // allow arrow keys + timeout(0); // no blocking on getch() + curs_set(0); // set the cursor to invisible + init_colors(); // setup tetris colors + } + tg = tg_create(rs,22, 10); + prevlevel = tg->level; + // Create windows for each section of the interface. + board = newwin(tg->rows + 2, 2 * tg->cols + 2, 0, 0); + next = newwin(6, 10, 0, 2 * (tg->cols + 1) + 1); + hold = newwin(6, 10, 7, 2 * (tg->cols + 1) + 1); + score = newwin(6, 10, 14, 2 * (tg->cols + 1 ) + 1); + while ( running != 0 ) + { + running = tg_tick(rs,tg,move); + if ( 1 && (rs->guiflag != 0 || rs->sleeptime != 0) ) + { + display_board(board,tg); + display_piece(next,tg->next); + display_piece(hold,tg->stored); + display_score(score,tg); + } + if ( rs->guiflag != 0 ) + { +#ifdef STANDALONE + sleep_milli(15); + if ( (counter++ % 10) == 0 ) + doupdate(); + c = games_readevent(rs); + if ( c <= 0x7f || skipcount == 0x3fff ) + { + if ( skipcount > 0 ) + issue_games_events(rs,Gametxidstr,eventid-skipcount,skipcount | 0x4000); + if ( c <= 0x7f ) + issue_games_events(rs,Gametxidstr,eventid,c); + if ( tg->level != prevlevel ) + { + flushkeystrokes(rs,0); + prevlevel = tg->level; + } + skipcount = 0; + } else skipcount++; +#endif + } + else + { + if ( rs->replaydone != 0 ) + break; + if ( rs->sleeptime != 0 ) + { + sleep_milli(1); + if ( (counter++ % 20) == 0 ) + doupdate(); + } + if ( skipcount == 0 ) + { + c = games_readevent(rs); + //fprintf(stderr,"%04x score.%d level.%d\n",c,tg->points,tg->level); + if ( (c & 0x4000) == 0x4000 ) + { + skipcount = (c & 0x3fff); + c = 'S'; + } + } + if ( skipcount > 0 ) + skipcount--; + } + eventid++; + switch ( c ) + { + case 'h': + move = TM_LEFT; + break; + case 'l': + move = TM_RIGHT; + break; + case 'k': + move = TM_CLOCK; + break; + case 'j': + move = TM_DROP; + break; + case 'q': + running = false; + move = TM_NONE; + break; + /*case 'p': + wclear(board); + box(board, 0, 0); + wmove(board, tg->rows/2, (tg->cols*COLS_PER_CELL-6)/2); + wprintw(board, "PAUSED"); + wrefresh(board); + timeout(-1); + getch(); + timeout(0); + move = TM_NONE; + break; + case 's': + save(tg, board); + move = TM_NONE; + break;*/ + case ' ': + move = TM_HOLD; + break; + default: + move = TM_NONE; + } + } + return(tg); +} + +#ifdef STANDALONE +/* + Main tetris game! + */ +#include "dapps/dappstd.c" + + +char *clonestr(char *str) +{ + char *clone; int32_t len; + if ( str == 0 || str[0] == 0 ) + { + printf("warning cloning nullstr.%p\n",str); +#ifdef __APPLE__ + while ( 1 ) sleep(1); +#endif + str = (char *)""; + } + len = strlen(str); + clone = (char *)calloc(1,len+16); + strcpy(clone,str); + return(clone); +} + +int32_t issue_games_events(struct games_state *rs,char *gametxidstr,uint32_t eventid,gamesevent c) +{ + static FILE *fp; + char params[512],*retstr; cJSON *retjson,*resobj; int32_t retval = -1; + if ( fp == 0 ) + fp = fopen("events.log","wb"); + rs->buffered[rs->num++] = c; + if ( 0 ) + { + if ( sizeof(c) == 1 ) + sprintf(params,"[\"events\",\"17\",\"[%%22%02x%%22,%%22%s%%22,%u]\"]",c&0xff,gametxidstr,eventid); + else if ( sizeof(c) == 2 ) + sprintf(params,"[\"events\",\"17\",\"[%%22%04x%%22,%%22%s%%22,%u]\"]",c&0xffff,gametxidstr,eventid); + else if ( sizeof(c) == 4 ) + sprintf(params,"[\"events\",\"17\",\"[%%22%08x%%22,%%22%s%%22,%u]\"]",c&0xffffffff,gametxidstr,eventid); + else if ( sizeof(c) == 8 ) + sprintf(params,"[\"events\",\"17\",\"[%%22%016llx%%22,%%22%s%%22,%u]\"]",(long long)c,gametxidstr,eventid); + if ( (retstr= komodo_issuemethod(USERPASS,(char *)"cclib",params,GAMES_PORT)) != 0 ) + { + if ( (retjson= cJSON_Parse(retstr)) != 0 ) + { + if ( (resobj= jobj(retjson,(char *)"result")) != 0 ) + { + retval = 0; + if ( fp != 0 ) + { + fprintf(fp,"%s\n",jprint(resobj,0)); + fflush(fp); + } + } + free_json(retjson); + } else fprintf(fp,"error parsing %s\n",retstr); + free(retstr); + } else fprintf(fp,"error issuing method %s\n",params); + return(retval); + } else return(0); +} + +int tetris(int argc, char **argv) +{ + struct games_state *rs = &globalR; + int32_t c,skipcount=0; uint32_t eventid = 0; tetris_game *tg = 0; + memset(rs,0,sizeof(*rs)); + rs->guiflag = 1; + rs->sleeptime = 1; // non-zero to allow refresh() + if ( argc >= 2 && strlen(argv[2]) == 64 ) + { +#ifdef _WIN32 +#ifdef _MSC_VER + rs->origseed = _strtoui64(argv[1], NULL, 10); +#else + rs->origseed = atol(argv[1]); // windows, but not MSVC +#endif // _MSC_VER +#else + rs->origseed = atol(argv[1]); // non-windows +#endif // _WIN32 + rs->seed = rs->origseed; + if ( argc >= 3 ) + { + strcpy(Gametxidstr,argv[2]); + fprintf(stderr,"setplayerdata %s\n",Gametxidstr); + if ( games_setplayerdata(rs,Gametxidstr) < 0 ) + { + fprintf(stderr,"invalid gametxid, or already started\n"); + return(-1); + } + } + } else rs->seed = 777; + + /* Load file if given a filename. + if (argc >= 2) { + FILE *f = fopen(argv[1], "r"); + if (f == NULL) { + perror("tetris"); + exit(EXIT_FAILURE); + } + tg = tg_load(f); + fclose(f); + } else { + // Otherwise create new game. + tg = tg_create(rs,22, 10); + }*/ + + // Game loop + tg = (tetris_game *)gamesiterate(rs); + gamesbailout(rs); + // Deinitialize NCurses + wclear(stdscr); + endwin(); + // Output ending message. + printf("Game over!\n"); + printf("You finished with %d points on level %d.\n", tg->points, tg->level); + + // Deinitialize Tetris + tg_delete(tg); + return 0; +} + +#endif + diff --git a/src/cc/prices.cpp b/src/cc/prices.cpp index c53207562..8a52dcb37 100644 --- a/src/cc/prices.cpp +++ b/src/cc/prices.cpp @@ -1,3 +1,4 @@ + /****************************************************************************** * Copyright © 2014-2019 The SuperNET Developers. * * * @@ -13,442 +14,60 @@ * * ******************************************************************************/ -#include "CCassets.h" -#include "CCPrices.h" - -/* - Prices CC would best build on top of the oracles CC, ie. to combine payments for multiple oracles and to calculate a 51% protected price feed. - - We need to assume there is an oracle for a specific price. In the event there are more than one provider, the majority need to be within correlation distance to update a pricepoint. - - int64_t OraclePrice(int32_t height,uint256 reforacletxid,char *markeraddr,char *format); - - Using the above function, a consensus price can be obtained for a datasource. - - given an oracletxid, the marketaddr and format can be extracted to be used for future calls to OraclePrice. This allows to set a starting price and that in turn allows cash settled leveraged trading! - - Funds work like with dice, ie. there is a Prices plan that traders bet against. - - PricesFunding oracletxid, margin, priceaveraging, maxleverage, funding, longtoken, shorttoken, N [pubkeys] - - PricesBet -> oracletxid start with 'L', leverage, funding, direction - funds are locked into global CC address - it can be closed at anytime by the trader for cash settlement - the house account can close it if rekt - - Implementation Notes: - In order to eliminate the need for worrying about sybil attacks, each prices plan would be able to specific pubkey(s?) for whitelisted publishers. It would be possible to have a non-whitelisted plan that would use 50% correlation between publishers. - - delta neutral balancing of riskexposure: fabs(long exposure - short exposure) - bet +B at leverage L - absval(sum(+BLi) - sum(-Bli)) - - validate: update riskexposure and it needs to be <= funds - - PricesProfits: limit withdraw to funds in excess of riskexposure - PricesFinish: payout (if winning) and update riskexposure - need long/short exposure assets - - funding -> 1of2 CC global CC address and dealer address, exposure tokens to global 1of2 assets CC address - pricebet -> user funds and exposure token to 1of2 address. - pricewin -> winnings from dealer funds, exposure token back to global address - priceloss -> exposuretoken back to global address - - exposure address, funds address - - - -*/ - -// start of consensus code - -int64_t PricesOraclePrice(int64_t &rektprice,uint64_t mode,uint256 oracletxid,std::vectorpubkeys,int32_t dir,int64_t amount,int32_t leverage) +// game specific code for daemon +void games_packitemstr(char *packitemstr,struct games_packitem *item) { - int64_t price; - // howto ensure price when block it confirms it not known - // get price from oracle + current chaintip - // normalize leveraged amount - if ( dir > 0 ) - rektprice = price * leverage / (leverage-1); - else rektprice = price * (leverage-1) / leverage; - return(price); + strcpy(packitemstr,""); } -CScript EncodePricesFundingOpRet(uint8_t funcid,CPubKey planpk,uint256 oracletxid,uint256 longtoken,uint256 shorttoken,int32_t millimargin,uint64_t mode,int32_t maxleverage,std::vector pubkeys,uint256 bettoken) +int64_t games_cashout(struct games_player *P) { - CScript opret; - fprintf(stderr,"implement EncodePricesFundingOpRet\n"); - return(opret); + int32_t dungeonlevel = P->dungeonlevel; int64_t mult=1000,cashout = 0; + cashout = (uint64_t)P->gold * mult * dungeonlevel * dungeonlevel; + return(cashout); } -uint8_t DecodePricesFundingOpRet(CScript scriptPubKey,CPubKey &planpk,uint256 &oracletxid,uint256 &longtoken,uint256 &shorttoken,int32_t &millimargin,uint64_t &mode,int32_t &maxleverage,std::vector &pubkeys,uint256 &bettoken) +void tetrisplayerjson(UniValue &obj,struct games_player *P) { - fprintf(stderr,"implement DecodePricesFundingOpRet\n"); + obj.push_back(Pair("packsize",(int64_t)P->packsize)); + obj.push_back(Pair("hitpoints",(int64_t)P->hitpoints)); + obj.push_back(Pair("strength",(int64_t)(P->strength&0xffff))); + obj.push_back(Pair("maxstrength",(int64_t)(P->strength>>16))); + obj.push_back(Pair("level",(int64_t)P->level)); + obj.push_back(Pair("experience",(int64_t)P->experience)); + obj.push_back(Pair("dungeonlevel",(int64_t)P->dungeonlevel)); +} + +int32_t disp_gamesplayer(char *str,struct games_player *P) +{ + str[0] = 0; + //if ( P->gold <= 0 )//|| P->hitpoints <= 0 || (P->strength&0xffff) <= 0 || P->level <= 0 || P->experience <= 0 || P->dungeonlevel <= 0 ) + // return(-1); + sprintf(str," <- playerdata: gold.%d hp.%d strength.%d/%d level.%d exp.%d dl.%d",P->gold,P->hitpoints,P->strength&0xffff,P->strength>>16,P->level,P->experience,P->dungeonlevel); return(0); } -bool PricesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn) +int32_t games_payloadrecv(CPubKey pk,uint32_t timestamp,std::vector payload) { - int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks; bool retval; uint256 txid; uint8_t hash[32]; char str[65],destaddr[64]; - - return true; // TODO remove, for test dual-evals - - return eval->Invalid("no validation yet"); - std::vector > txids; - numvins = tx.vin.size(); - numvouts = tx.vout.size(); - preventCCvins = preventCCvouts = -1; - if ( numvouts < 1 ) - return eval->Invalid("no vouts"); - else + uint256 gametxid; int32_t i,len; char str[67]; uint32_t eventid = 0; + if ( (len= payload.size()) > 36 ) { - for (i=0; iInvalid("illegal normal vini"); - } - } - //fprintf(stderr,"check amounts\n"); - //if ( PricesExactAmounts(cp,eval,tx,1,10000) == false ) - { - fprintf(stderr,"Pricesget invalid amount\n"); - return false; - } - //else - { - txid = tx.GetHash(); - memcpy(hash,&txid,sizeof(hash)); - retval = PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts); - if ( retval != 0 ) - fprintf(stderr,"Pricesget validated\n"); - else fprintf(stderr,"Pricesget invalid\n"); - return(retval); - } - } -} -// end of consensus code - -// helper functions for rpc calls in rpcwallet.cpp - -int64_t AddTokensInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,char *destaddr,uint256 tolenid,int64_t total,int32_t maxinputs) -{ - // add threshold check - int64_t nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector origpubkey; CTransaction vintx; int32_t vout,n = 0; - std::vector > unspentOutputs; - SetCCunspents(unspentOutputs,destaddr); - for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) - { - txid = it->first.txhash; - vout = (int32_t)it->first.index; - // need to prevent dup - if ( GetTransaction(txid,vintx,hashBlock,false) != 0 && vout < vintx.vout.size() ) - { - // need to verify assetid - if ( (nValue= vintx.vout[vout].nValue) >= 10000 && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) - { - if ( total != 0 && maxinputs != 0 ) - mtx.vin.push_back(CTxIn(txid,vout,CScript())); - nValue = it->second.satoshis; - totalinputs += nValue; - n++; - if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) ) - break; - } - } - } - return(totalinputs); + len -= 36; + for (i=0; i<32; i++) + ((uint8_t *)&gametxid)[i] = payload[len+i]; + eventid = (uint32_t)payload[len+32]; + eventid |= (uint32_t)payload[len+33] << 8; + eventid |= (uint32_t)payload[len+34] << 16; + eventid |= (uint32_t)payload[len+35] << 24; + //for (i=0; i > addressIndex; struct CCcontract_info *cp,C; uint64_t mode; int32_t margin,maxleverage; std::vectorpubkeys; uint256 txid,hashBlock,oracletxid,longtoken,shorttoken,bettoken; CPubKey planpk,pricespk; char str[65]; CTransaction vintx; - cp = CCinit(&C,EVAL_PRICES); - pricespk = GetUnspendable(cp,0); - SetCCtxids(addressIndex,cp->normaladdr); - for (std::vector >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) - { - txid = it->first.txhash; - if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) - { - if ( vintx.vout.size() > 0 && DecodePricesFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,planpk,oracletxid,longtoken,shorttoken,margin,mode,maxleverage,pubkeys,bettoken) == 'F' ) - { - result.push_back(uint256_str(str,txid)); - } - } - } - return(result); + return(true); } -// longtoken satoshis limits long exposure -// shorttoken satoshis limits short exposure -// both must be in the 1of2 CC address with its total supply -// bettoken -std::string PricesCreateFunding(uint64_t txfee,uint256 bettoken,uint256 oracletxid,uint64_t margin,uint64_t mode,uint256 longtoken,uint256 shorttoken,int32_t maxleverage,int64_t funding,std::vector pubkeys) -{ - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CTransaction oracletx; int64_t fullsupply,inputs,CCchange=0; uint256 hashBlock; char str[65],coinaddr[64],houseaddr[64]; CPubKey mypk,pricespk; int32_t i,N,numvouts; struct CCcontract_info *cp,C; - if ( funding < 100*COIN || maxleverage <= 0 || maxleverage > 10000 ) - { - CCerror = "invalid parameter error"; - fprintf(stderr,"%s\n", CCerror.c_str() ); - return(""); - } - cp = CCinit(&C,EVAL_PRICES); - if ( txfee == 0 ) - txfee = 10000; - mypk = pubkey2pk(Mypubkey()); - pricespk = GetUnspendable(cp,0); - if ( (N= (int32_t)pubkeys.size()) || N > 15 ) - { - fprintf(stderr,"too many pubkeys N.%d\n",N); - return(""); - } - for (i=0; i 0 ) - { - mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); - mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(pricespk)) << OP_CHECKSIG)); - return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodePricesFundingOpRet('F',mypk,oracletxid,longtoken,shorttoken,margin,mode,maxleverage,pubkeys,bettoken))); - } - else - { - CCerror = "cant find enough inputs"; - fprintf(stderr,"%s\n", CCerror.c_str() ); - } - return(""); -} - -UniValue PricesInfo(uint256 fundingtxid) -{ - UniValue result(UniValue::VOBJ),a(UniValue::VARR); CPubKey pricespk,planpk; uint256 hashBlock,oracletxid,longtoken,shorttoken,bettoken; CTransaction vintx; int64_t balance,supply,exposure; uint64_t funding,mode; int32_t i,margin,maxleverage; char numstr[65],houseaddr[64],exposureaddr[64],str[65]; std::vectorpubkeys; struct CCcontract_info *cp,C; - cp = CCinit(&C,EVAL_PRICES); - pricespk = GetUnspendable(cp,0); - if ( GetTransaction(fundingtxid,vintx,hashBlock,false) == 0 ) - { - fprintf(stderr,"cant find fundingtxid\n"); - ERR_RESULT("cant find fundingtxid"); - return(result); - } - if ( vintx.vout.size() > 0 && DecodePricesFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,planpk,oracletxid,longtoken,shorttoken,margin,mode,maxleverage,pubkeys,bettoken) == 'F' ) - { - result.push_back(Pair("result","success")); - result.push_back(Pair("fundingtxid",uint256_str(str,fundingtxid))); - result.push_back(Pair("bettoken",uint256_str(str,bettoken))); - result.push_back(Pair("oracletxid",uint256_str(str,oracletxid))); - sprintf(numstr,"%.3f",(double)margin/1000); - result.push_back(Pair("profitmargin",numstr)); - result.push_back(Pair("maxleverage",maxleverage)); - result.push_back(Pair("mode",(int64_t)mode)); - for (i=0; ipubkeys; - if ( amount < 10000 ) - { - CCerror = "amount must be positive"; - fprintf(stderr,"%s\n", CCerror.c_str() ); - return(""); - } - cp = CCinit(&C,EVAL_PRICES); - if ( txfee == 0 ) - txfee = 10000; - mypk = pubkey2pk(Mypubkey()); - pricespk = GetUnspendable(cp,0); - GetCCaddress(cp,myaddr,mypk); - if ( GetTransaction(fundingtxid,tx,hashBlock,false) == 0 ) - { - fprintf(stderr,"cant find fundingtxid\n"); - return(""); - } - if ( tx.vout.size() > 0 && DecodePricesFundingOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,planpk,oracletxid,longtoken,shorttoken,margin,mode,maxleverage,pubkeys,bettoken) == 'F' && bettoken == refbettoken ) - { - GetCCaddress1of2(cp,houseaddr,pricespk,planpk); - if ( AddNormalinputs(mtx,mypk,2*txfee,3) > 0 ) - { - if ( (inputs= AddTokensInputs(cp,mtx,myaddr,bettoken,amount,60)) >= amount ) - { - mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,amount,pricespk,planpk)); - mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(planpk)) << OP_CHECKSIG)); - if ( inputs > amount+txfee ) - CCchange = (inputs - amount); - mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange,mypk)); - // add addr2 - - std::vector voutTokenPubkeysEmpty; //TODO: add token vout pubkeys - return(FinalizeCCTx(0,cp,mtx,mypk,txfee, - EncodeTokenOpRet(bettoken, voutTokenPubkeysEmpty, - std::make_pair(OPRETID_ASSETSDATA, EncodeAssetOpRet('t',/*bettoken,*/zeroid, 0, Mypubkey()))))); - } - else - { - CCerror = "cant find enough bet inputs"; - fprintf(stderr,"%s\n", CCerror.c_str() ); - } - } - else - { - CCerror = "cant find enough inputs"; - fprintf(stderr,"%s\n", CCerror.c_str() ); - } - } - return(""); -} - -std::string PricesBet(uint64_t txfee,uint256 refbettoken,uint256 fundingtxid,int64_t amount,int32_t leverage) -{ - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - struct CCcontract_info *cp,C; CPubKey pricespk,planpk,mypk; uint256 hashBlock,oracletxid,longtoken,shorttoken,tokenid,bettoken; CTransaction tx; int64_t balance,supply,exposure,inputs,inputs2,longexposure,netexposure,shortexposure,CCchange = 0,CCchange2 = 0; uint64_t funding,mode; int32_t dir,margin,maxleverage; char houseaddr[64],myaddr[64],exposureaddr[64]; std::vectorpubkeys; - if ( amount < 0 ) - { - amount = -amount; - dir = -1; - } else dir = 1; - cp = CCinit(&C,EVAL_PRICES); - if ( txfee == 0 ) - txfee = 10000; - mypk = pubkey2pk(Mypubkey()); - pricespk = GetUnspendable(cp,0); - GetCCaddress(cp,myaddr,mypk); - if ( GetTransaction(fundingtxid,tx,hashBlock,false) == 0 ) - { - fprintf(stderr,"cant find fundingtxid\n"); - return(""); - } - if ( tx.vout.size() > 0 && DecodePricesFundingOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,planpk,oracletxid,longtoken,shorttoken,margin,mode,maxleverage,pubkeys,bettoken) == 'F' && bettoken == refbettoken ) - { - if ( leverage > maxleverage || leverage < 1 ) - { - fprintf(stderr,"illegal leverage\n"); - return(""); - } - GetCCaddress1of2(cp,houseaddr,pricespk,planpk); - GetCCaddress1of2(cp,exposureaddr,pricespk,pricespk); - if ( dir < 0 ) - tokenid = shorttoken; - else tokenid = longtoken; - exposure = leverage * amount; - longexposure = CCtoken_balance(exposureaddr,longtoken); - shortexposure = CCtoken_balance(exposureaddr,shorttoken); - netexposure = (longexposure - shortexposure + exposure*dir); - if ( netexposure < 0 ) - netexposure = -netexposure; - balance = CCtoken_balance(myaddr,bettoken) / COIN; - if ( balance < netexposure*9/10 ) // 10% extra room for dynamically closed bets in wrong direction - { - fprintf(stderr,"balance %lld < 90%% netexposure %lld, refuse bet\n",(long long)balance,(long long)netexposure); - return(""); - } - if ( AddNormalinputs(mtx,mypk,txfee,3) > 0 ) - { - if ( (inputs= AddTokensInputs(cp,mtx,houseaddr,tokenid,exposure,30)) >= exposure ) - { - if ( (inputs2= AddTokensInputs(cp,mtx,myaddr,bettoken,amount,30)) >= amount ) - { - mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,amount,pricespk,planpk)); - mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,exposure,pricespk,pricespk)); - if ( inputs > exposure+txfee ) - CCchange = (inputs - exposure); - if ( inputs2 > amount+txfee ) - CCchange2 = (inputs2 - amount); - mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,CCchange,pricespk,planpk)); - mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange2,mypk)); - // add addr2 and addr3 - //return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodePricesExtra('T',tokenid,bettoken,zeroid,dir*leverage))); - CScript opret; - return(FinalizeCCTx(0,cp,mtx,mypk,txfee,opret)); - } - else - { - fprintf(stderr,"cant find enough bettoken inputs\n"); - return(""); - } - } - else - { - fprintf(stderr,"cant find enough exposure inputs\n"); - return(""); - } - } - else - { - CCerror = "cant find enough inputsB"; - fprintf(stderr,"%s\n", CCerror.c_str() ); - } - } - return(""); -} - -UniValue PricesStatus(uint64_t txfee,uint256 refbettoken,uint256 fundingtxid,uint256 bettxid) -{ - UniValue result(UniValue::VOBJ); - // get height of bettxid - // get price and rekt - // get current height and price - // what about if rekt in the past? - return(result); -} - -std::string PricesFinish(uint64_t txfee,uint256 refbettoken,uint256 fundingtxid,uint256 bettxid) -{ - return(""); -} - - - diff --git a/src/cc/prices.h b/src/cc/prices.h new file mode 100644 index 000000000..ae1c46821 --- /dev/null +++ b/src/cc/prices.h @@ -0,0 +1,208 @@ + +#ifndef H_PRICES_H +#define H_PRICES_H + +/***************************************************************************/ +/** https://github.com/brenns10/tetris + @file main.c + @author Stephen Brennan + @date Created Wednesday, 10 June 2015 + @brief Main program for tetris. + @copyright Copyright (c) 2015, Stephen Brennan. Released under the Revised + BSD License. See LICENSE.txt for details. + *******************************************************************************/ + +/* + Convert a tetromino type to its corresponding cell. + */ +#define TYPE_TO_CELL(x) ((x)+1) + +/* + Strings for how you would print a tetris board. + */ +#define TC_EMPTY_STR " " +#define TC_BLOCK_STR "\u2588" + +/* + Questions about a tetris cell. + */ +#define TC_IS_EMPTY(x) ((x) == TC_EMPTY) +#define TC_IS_FILLED(x) (!TC_IS_EMPTY(x)) + +/* + How many cells in a tetromino? + */ +#define TETRIS 4 +/* + How many tetrominos? + */ +#define NUM_TETROMINOS 7 +/* + How many orientations of a tetromino? + */ +#define NUM_ORIENTATIONS 4 + +/* + Level constants. + */ +#define MAX_LEVEL 19 +#define LINES_PER_LEVEL 10 + +/* + A "cell" is a 1x1 block within a tetris board. + */ +typedef enum { + TC_EMPTY, TC_CELLI, TC_CELLJ, TC_CELLL, TC_CELLO, TC_CELLS, TC_CELLT, TC_CELLZ +} tetris_cell; + +/* + A "type" is a type/shape of a tetromino. Not including orientation. + */ +typedef enum { + TET_I, TET_J, TET_L, TET_O, TET_S, TET_T, TET_Z +} tetris_type; + +/* + A row,column pair. Negative numbers allowed, because we need them for + offsets. + */ +typedef struct { + int row; + int col; +} tetris_location; + +/* + A "block" is a struct that contains information about a tetromino. + Specifically, what type it is, what orientation it has, and where it is. + */ +typedef struct { + int typ; + int ori; + tetris_location loc; +} tetris_block; + +/* + All possible moves to give as input to the game. + */ +typedef enum { + TM_LEFT, TM_RIGHT, TM_CLOCK, TM_COUNTER, TM_DROP, TM_HOLD, TM_NONE +} tetris_move; + +/* + A game object! + */ +typedef struct { + /* + Game board stuff: + */ + int rows; + int cols; + /* + Scoring information: + */ + int points; + int level; + /* + Falling block is the one currently going down. Next block is the one that + will be falling after this one. Stored is the block that you can swap out. + */ + tetris_block falling; + tetris_block next; + tetris_block stored; + /* + Number of game ticks until the block will move down. + */ + int ticks_till_gravity; + /* + Number of lines until you advance to the next level. + */ + int lines_remaining; + char board[]; +} tetris_game; + +/* + This array stores all necessary information about the cells that are filled by + each tetromino. The first index is the type of the tetromino (i.e. shape, + e.g. I, J, Z, etc.). The next index is the orientation (0-3). The final + array contains 4 tetris_location objects, each mapping to an offset from a + point on the upper left that is the tetromino "origin". + */ +extern const tetris_location TETROMINOS[NUM_TETROMINOS][NUM_ORIENTATIONS][TETRIS]; + +/* + This array tells you how many ticks per gravity by level. Decreases as level + increases, to add difficulty. + */ +extern const int GRAVITY_LEVEL[MAX_LEVEL+1]; + +// Data structure manipulation. +void tg_init(tetris_game *obj, int rows, int cols); +tetris_game *tg_create(struct games_state *rs,int rows, int cols); +void tg_destroy(tetris_game *obj); +void tg_delete(tetris_game *obj); +tetris_game *tg_load(FILE *f); +void tg_save(tetris_game *obj, FILE *f); + +// Public methods not related to memory: +char tg_get(tetris_game *obj, int row, int col); +bool tg_check(tetris_game *obj, int row, int col); +bool tg_tick(struct games_state *rs,tetris_game *obj, tetris_move move); +void tg_print(tetris_game *obj, FILE *f); + +/****************************************************************************** + * Copyright © 2014-2019 The SuperNET Developers. * + * * + * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * + * the top-level directory of this distribution for the individual copyright * + * holder information and the developer policies on copyright and licensing. * + * * + * Unless otherwise agreed in a custom licensing agreement, no part of the * + * SuperNET software, including this file may be copied, modified, propagated * + * or distributed except according to the terms contained in the LICENSE file * + * * + * Removal or modification of this copyright notice is prohibited. * + * * + ******************************************************************************/ +#define GAMENAME "prices" // name of executable +#define GAMEMAIN prices // main program of game +#define GAMEPLAYERJSON pricesplayerjson // displays game specific json +#define GAMEDATA pricesdata // extracts data from game specific variables into games_state +#define CHAINNAME "PRICES" // -ac_name= +typedef uint64_t gamesevent; // can be 8, 16, 32, or 64 bits + +#define MAXPACK 23 +struct games_packitem +{ + int32_t type,launch,count,which,hplus,dplus,arm,flags,group; + char damage[8],hurldmg[8]; +}; + +struct games_player +{ + int32_t gold,hitpoints,strength,level,experience,packsize,dungeonlevel,amulet; + struct games_packitem gamespack[MAXPACK]; +}; + +struct games_state +{ + uint64_t seed,origseed; + char *keystrokeshex; + uint32_t needflush,replaydone; + int32_t numkeys,ind,num,guiflag,counter,sleeptime,playersize,restoring,lastnum; + FILE *logfp; + struct games_player P; + gamesevent buffered[5000],*keystrokes; + uint8_t playerdata[8192]; +}; +extern struct games_state globalR; +void *gamesiterate(struct games_state *rs); +int32_t flushkeystrokes(struct games_state *rs,int32_t waitflag); + +void games_packitemstr(char *packitemstr,struct games_packitem *item); +uint64_t _games_rngnext(uint64_t initseed); +int32_t games_replay2(uint8_t *newdata,uint64_t seed,gamesevent *keystrokes,int32_t num,struct games_player *player,int32_t sleepmillis); +gamesevent games_revendian(gamesevent revx); +int32_t disp_gamesplayer(char *str,struct games_player *P); + +#endif +