From fa0a35aa5a2dac695e4452587f5c9b18e5943be0 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 00:11:36 -1100 Subject: [PATCH] Split out Tetris --- src/cc/dapps/dappstd.c | 1020 +++++++++++++++++ src/cc/gamescc.cpp | 2048 +--------------------------------- src/cc/gamescc.h | 32 +- src/cc/tetris.c | 758 +++++++++++++ src/cc/tetris.cpp | 2376 +--------------------------------------- src/cc/tetris.h | 197 ++++ 6 files changed, 2046 insertions(+), 4385 deletions(-) create mode 100644 src/cc/dapps/dappstd.c create mode 100644 src/cc/tetris.c create mode 100644 src/cc/tetris.h diff --git a/src/cc/dapps/dappstd.c b/src/cc/dapps/dappstd.c new file mode 100644 index 000000000..ec8045b2f --- /dev/null +++ b/src/cc/dapps/dappstd.c @@ -0,0 +1,1020 @@ +/****************************************************************************** + * 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. * + * * + ******************************************************************************/ + +// requires CHAINNAME and GAMEMAIN() to be #defined + +#include +#include +#include +#include +#include +#include +#include + +char USERPASS[8192]; uint16_t GAMES_PORT; +char Gametxidstr[67]; +char *clonestr(char *str); + +#define MAXSTR 1024 +char whoami[MAXSTR]; + +#define SMALLVAL 0.000000000000001 +#define SATOSHIDEN ((uint64_t)100000000L) +#define dstr(x) ((double)(x) / SATOSHIDEN) +#define KOMODO_ASSETCHAIN_MAXLEN 65 +char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN],IPADDRESS[100]; + +#ifndef _BITS256 +#define _BITS256 +union _bits256 { uint8_t bytes[32]; uint16_t ushorts[16]; uint32_t uints[8]; uint64_t ulongs[4]; uint64_t txid; }; +typedef union _bits256 bits256; +#endif + +#ifdef _WIN32 +#ifdef _MSC_VER +int gettimeofday(struct timeval * tp, struct timezone * tzp) +{ + // Note: some broken versions only have 8 trailing zero's, the correct epoch has 9 trailing zero's + static const uint64_t EPOCH = ((uint64_t)116444736000000000ULL); + + SYSTEMTIME system_time; + FILETIME file_time; + uint64_t time; + + GetSystemTime(&system_time); + SystemTimeToFileTime(&system_time, &file_time); + time = ((uint64_t)file_time.dwLowDateTime); + time += ((uint64_t)file_time.dwHighDateTime) << 32; + + tp->tv_sec = (long)((time - EPOCH) / 10000000L); + tp->tv_usec = (long)(system_time.wMilliseconds * 1000); + return 0; +} +#endif // _MSC_VER +#endif + +double OS_milliseconds() +{ + struct timeval tv; double millis; +#ifdef __MINGW32__ + mingw_gettimeofday(&tv,NULL); +#else + gettimeofday(&tv,NULL); +#endif + millis = ((double)tv.tv_sec * 1000. + (double)tv.tv_usec / 1000.); + //printf("tv_sec.%ld usec.%d %f\n",tv.tv_sec,tv.tv_usec,millis); + return(millis); +} + +int32_t _unhex(char c) +{ + if ( c >= '0' && c <= '9' ) + return(c - '0'); + else if ( c >= 'a' && c <= 'f' ) + return(c - 'a' + 10); + else if ( c >= 'A' && c <= 'F' ) + return(c - 'A' + 10); + return(-1); +} + +int32_t is_hexstr(char *str,int32_t n) +{ + int32_t i; + if ( str == 0 || str[0] == 0 ) + return(0); + for (i=0; str[i]!=0; i++) + { + if ( n > 0 && i >= n ) + break; + if ( _unhex(str[i]) < 0 ) + break; + } + if ( n == 0 ) + return(i); + return(i == n); +} + +int32_t unhex(char c) +{ + int32_t hex; + if ( (hex= _unhex(c)) < 0 ) + { + //printf("unhex: illegal hexchar.(%c)\n",c); + } + return(hex); +} + +unsigned char _decode_hex(char *hex) { return((unhex(hex[0])<<4) | unhex(hex[1])); } + +int32_t decode_hex(uint8_t *bytes,int32_t n,char *hex) +{ + int32_t adjust,i = 0; + //printf("decode.(%s)\n",hex); + if ( is_hexstr(hex,n) <= 0 ) + { + memset(bytes,0,n); + return(n); + } + if ( hex[n-1] == '\n' || hex[n-1] == '\r' ) + hex[--n] = 0; + if ( n == 0 || (hex[n*2+1] == 0 && hex[n*2] != 0) ) + { + if ( n > 0 ) + { + bytes[0] = unhex(hex[0]); + printf("decode_hex n.%d hex[0] (%c) -> %d hex.(%s) [n*2+1: %d] [n*2: %d %c] len.%ld\n",n,hex[0],bytes[0],hex,hex[n*2+1],hex[n*2],hex[n*2],(long)strlen(hex)); + } + bytes++; + hex++; + adjust = 1; + } else adjust = 0; + if ( n > 0 ) + { + for (i=0; i>4) & 0xf); + hexbytes[i*2 + 1] = hexbyte(message[i] & 0xf); + //printf("i.%d (%02x) [%c%c]\n",i,message[i],hexbytes[i*2],hexbytes[i*2+1]); + } + hexbytes[len*2] = 0; + //printf("len.%ld\n",len*2+1); + return((int32_t)len*2+1); +} + +char *bits256_str(char hexstr[65],bits256 x) +{ + init_hexbytes_noT(hexstr,x.bytes,sizeof(x)); + return(hexstr); +} + +long _stripwhite(char *buf,int accept) +{ + int32_t i,j,c; + if ( buf == 0 || buf[0] == 0 ) + return(0); + for (i=j=0; buf[i]!=0; i++) + { + buf[j] = c = buf[i]; + if ( c == accept || (c != ' ' && c != '\n' && c != '\r' && c != '\t' && c != '\b') ) + j++; + } + buf[j] = 0; + return(j); +} + +char *parse_conf_line(char *line,char *field) +{ + line += strlen(field); + for (; *line!='='&&*line!=0; line++) + break; + if ( *line == 0 ) + return(0); + if ( *line == '=' ) + line++; + while ( line[strlen(line)-1] == '\r' || line[strlen(line)-1] == '\n' || line[strlen(line)-1] == ' ' ) + line[strlen(line)-1] = 0; + //printf("LINE.(%s)\n",line); + _stripwhite(line,0); + return(clonestr(line)); +} + +int32_t safecopy(char *dest,char *src,long len) +{ + int32_t i = -1; + if ( src != 0 && dest != 0 && src != dest ) + { + if ( dest != 0 ) + memset(dest,0,len); + for (i=0; i buflen ) + { + *allocsizep = filesize; + *bufp = buf = (uint8_t *)realloc(buf,(long)*allocsizep+64); + } + rewind(fp); + if ( buf == 0 ) + printf("Null buf ???\n"); + else + { + if ( fread(buf,1,(long)filesize,fp) != (unsigned long)filesize ) + printf("error reading filesize.%ld\n",(long)filesize); + buf[filesize] = 0; + } + fclose(fp); + *lenp = filesize; + //printf("loaded.(%s)\n",buf); + } //else printf("OS_loadfile couldnt load.(%s)\n",fname); + return(buf); +} + +uint8_t *OS_fileptr(long *allocsizep,char *fname) +{ + long filesize = 0; uint8_t *buf = 0; void *retptr; + *allocsizep = 0; + retptr = OS_loadfile(fname,&buf,&filesize,allocsizep); + return((uint8_t *)retptr); +} + +struct MemoryStruct { char *memory; size_t size; }; +struct return_string { char *ptr; size_t len; }; + +// return data from the server +#define CURL_GLOBAL_ALL (CURL_GLOBAL_SSL|CURL_GLOBAL_WIN32) +#define CURL_GLOBAL_SSL (1<<0) +#define CURL_GLOBAL_WIN32 (1<<1) + + +/************************************************************************ + * + * Initialize the string handler so that it is thread safe + * + ************************************************************************/ + +void init_string(struct return_string *s) +{ + s->len = 0; + s->ptr = (char *)calloc(1,s->len+1); + if ( s->ptr == NULL ) + { + fprintf(stderr,"init_string malloc() failed\n"); + exit(-1); + } + s->ptr[0] = '\0'; +} + +/************************************************************************ + * + * Use the "writer" to accumulate text until done + * + ************************************************************************/ + +size_t accumulatebytes(void *ptr,size_t size,size_t nmemb,struct return_string *s) +{ + size_t new_len = s->len + size*nmemb; + s->ptr = (char *)realloc(s->ptr,new_len+1); + if ( s->ptr == NULL ) + { + fprintf(stderr, "accumulate realloc() failed\n"); + exit(-1); + } + memcpy(s->ptr+s->len,ptr,size*nmemb); + s->ptr[new_len] = '\0'; + s->len = new_len; + return(size * nmemb); +} + +/************************************************************************ + * + * return the current system time in milliseconds + * + ************************************************************************/ + +#define EXTRACT_BITCOIND_RESULT // if defined, ensures error is null and returns the "result" field +#ifdef EXTRACT_BITCOIND_RESULT + +/************************************************************************ + * + * perform post processing of the results + * + ************************************************************************/ + +char *post_process_bitcoind_RPC(char *debugstr,char *command,char *rpcstr,char *params) +{ + long i,j,len; char *retstr = 0; cJSON *json,*result,*error; + //printf("<<<<<<<<<<< bitcoind_RPC: %s post_process_bitcoind_RPC.%s.[%s]\n",debugstr,command,rpcstr); + if ( command == 0 || rpcstr == 0 || rpcstr[0] == 0 ) + { + if ( strcmp(command,"signrawtransaction") != 0 ) + printf("<<<<<<<<<<< bitcoind_RPC: %s post_process_bitcoind_RPC.%s.[%s]\n",debugstr,command,rpcstr); + return(rpcstr); + } + json = cJSON_Parse(rpcstr); + if ( json == 0 ) + { + printf("<<<<<<<<<<< bitcoind_RPC: %s post_process_bitcoind_RPC.%s can't parse.(%s) params.(%s)\n",debugstr,command,rpcstr,params); + free(rpcstr); + return(0); + } + result = cJSON_GetObjectItem(json,"result"); + error = cJSON_GetObjectItem(json,"error"); + if ( error != 0 && result != 0 ) + { + if ( (error->type&0xff) == cJSON_NULL && (result->type&0xff) != cJSON_NULL ) + { + retstr = cJSON_Print(result); + len = strlen(retstr); + if ( retstr[0] == '"' && retstr[len-1] == '"' ) + { + for (i=1,j=0; itype&0xff) != cJSON_NULL || (result->type&0xff) != cJSON_NULL ) + { + if ( strcmp(command,"signrawtransaction") != 0 ) + printf("<<<<<<<<<<< bitcoind_RPC: %s post_process_bitcoind_RPC (%s) error.%s\n",debugstr,command,rpcstr); + } + free(rpcstr); + } else retstr = rpcstr; + free_json(json); + //fprintf(stderr,"<<<<<<<<<<< bitcoind_RPC: postprocess returns.(%s)\n",retstr); + return(retstr); +} +#endif + +#ifdef _WIN32 +#ifdef _MSC_VER +#define sleep(x) Sleep(1000*(x)) +#endif +#endif + +/************************************************************************ + * + * perform the query + * + ************************************************************************/ + +char *bitcoind_RPC(char **retstrp,char *debugstr,char *url,char *userpass,char *command,char *params) +{ + static int didinit,count,count2; static double elapsedsum,elapsedsum2; + struct curl_slist *headers = NULL; struct return_string s; CURLcode res; CURL *curl_handle; + char *bracket0,*bracket1,*databuf = 0; long len; int32_t specialcase,numretries; double starttime; + if ( didinit == 0 ) + { + didinit = 1; + curl_global_init(CURL_GLOBAL_ALL); //init the curl session + } + numretries = 0; + if ( debugstr != 0 && strcmp(debugstr,"BTCD") == 0 && command != 0 && strcmp(command,"SuperNET") == 0 ) + specialcase = 1; + else specialcase = 0; + if ( url[0] == 0 ) + strcpy(url,"http://127.0.0.1:7876/nxt"); + if ( specialcase != 0 && 0 ) + printf("<<<<<<<<<<< bitcoind_RPC: debug.(%s) url.(%s) command.(%s) params.(%s)\n",debugstr,url,command,params); +try_again: + if ( retstrp != 0 ) + *retstrp = 0; + starttime = OS_milliseconds(); + curl_handle = curl_easy_init(); + init_string(&s); + headers = curl_slist_append(0,"Expect:"); + + curl_easy_setopt(curl_handle,CURLOPT_USERAGENT,"mozilla/4.0");//"Mozilla/4.0 (compatible; )"); + curl_easy_setopt(curl_handle,CURLOPT_HTTPHEADER, headers); + curl_easy_setopt(curl_handle,CURLOPT_URL, url); + curl_easy_setopt(curl_handle,CURLOPT_WRITEFUNCTION, (void *)accumulatebytes); // send all data to this function + curl_easy_setopt(curl_handle,CURLOPT_WRITEDATA, &s); // we pass our 's' struct to the callback + curl_easy_setopt(curl_handle,CURLOPT_NOSIGNAL, 1L); // supposed to fix "Alarm clock" and long jump crash + curl_easy_setopt(curl_handle,CURLOPT_NOPROGRESS, 1L); // no progress callback + if ( strncmp(url,"https",5) == 0 ) + { + curl_easy_setopt(curl_handle,CURLOPT_SSL_VERIFYPEER,0); + curl_easy_setopt(curl_handle,CURLOPT_SSL_VERIFYHOST,0); + } + if ( userpass != 0 ) + curl_easy_setopt(curl_handle,CURLOPT_USERPWD, userpass); + databuf = 0; + if ( params != 0 ) + { + if ( command != 0 && specialcase == 0 ) + { + len = strlen(params); + if ( len > 0 && params[0] == '[' && params[len-1] == ']' ) { + bracket0 = bracket1 = (char *)""; + } + else + { + bracket0 = (char *)"["; + bracket1 = (char *)"]"; + } + + databuf = (char *)malloc(256 + strlen(command) + strlen(params)); + sprintf(databuf,"{\"id\":\"jl777\",\"method\":\"%s\",\"params\":%s%s%s}",command,bracket0,params,bracket1); + //printf("url.(%s) userpass.(%s) databuf.(%s)\n",url,userpass,databuf); + // + } //else if ( specialcase != 0 ) fprintf(stderr,"databuf.(%s)\n",params); + curl_easy_setopt(curl_handle,CURLOPT_POST,1L); + if ( databuf != 0 ) + curl_easy_setopt(curl_handle,CURLOPT_POSTFIELDS,databuf); + else curl_easy_setopt(curl_handle,CURLOPT_POSTFIELDS,params); + } + //laststart = milliseconds(); + res = curl_easy_perform(curl_handle); + curl_slist_free_all(headers); + curl_easy_cleanup(curl_handle); + if ( databuf != 0 ) // clean up temporary buffer + { + free(databuf); + databuf = 0; + } + if ( res != CURLE_OK ) + { + numretries++; + if ( specialcase != 0 ) + { + printf("<<<<<<<<<<< bitcoind_RPC.(%s): BTCD.%s timeout params.(%s) s.ptr.(%s) err.%d\n",url,command,params,s.ptr,res); + free(s.ptr); + return(0); + } + else if ( numretries >= 1 ) + { + //printf("Maximum number of retries exceeded!\n"); + free(s.ptr); + return(0); + } + if ( (rand() % 1000) == 0 ) + printf( "curl_easy_perform() failed: %s %s.(%s %s), retries: %d\n",curl_easy_strerror(res),debugstr,url,command,numretries); + free(s.ptr); + sleep((1< (%s)\n",params,s.ptr); + count2++; + elapsedsum2 += (OS_milliseconds() - starttime); + if ( (count2 % 10000) == 0) + printf("%d: ave %9.6f | elapsed %.3f millis | NXT calls.(%s) cmd.(%s)\n",count2,elapsedsum2/count2,(double)(OS_milliseconds() - starttime),url,command); + return(s.ptr); + } + } + printf("bitcoind_RPC: impossible case\n"); + free(s.ptr); + return(0); +} + +static size_t WriteMemoryCallback(void *ptr,size_t size,size_t nmemb,void *data) +{ + size_t realsize = (size * nmemb); + struct MemoryStruct *mem = (struct MemoryStruct *)data; + mem->memory = (char *)((ptr != 0) ? realloc(mem->memory,mem->size + realsize + 1) : malloc(mem->size + realsize + 1)); + if ( mem->memory != 0 ) + { + if ( ptr != 0 ) + memcpy(&(mem->memory[mem->size]),ptr,realsize); + mem->size += realsize; + mem->memory[mem->size] = 0; + } + //printf("got %d bytes\n",(int32_t)(size*nmemb)); + return(realsize); +} + +char *curl_post(CURL **cHandlep,char *url,char *userpass,char *postfields,char *hdr0,char *hdr1,char *hdr2,char *hdr3) +{ + struct MemoryStruct chunk; CURL *cHandle; long code; struct curl_slist *headers = 0; + if ( (cHandle= *cHandlep) == NULL ) + *cHandlep = cHandle = curl_easy_init(); + else curl_easy_reset(cHandle); + //#ifdef DEBUG + //curl_easy_setopt(cHandle,CURLOPT_VERBOSE, 1); + //#endif + curl_easy_setopt(cHandle,CURLOPT_USERAGENT,"mozilla/4.0");//"Mozilla/4.0 (compatible; )"); + curl_easy_setopt(cHandle,CURLOPT_SSL_VERIFYPEER,0); + //curl_easy_setopt(cHandle,CURLOPT_SSLVERSION,1); + curl_easy_setopt(cHandle,CURLOPT_URL,url); + curl_easy_setopt(cHandle,CURLOPT_CONNECTTIMEOUT,10); + if ( userpass != 0 && userpass[0] != 0 ) + curl_easy_setopt(cHandle,CURLOPT_USERPWD,userpass); + if ( postfields != 0 && postfields[0] != 0 ) + { + curl_easy_setopt(cHandle,CURLOPT_POST,1); + curl_easy_setopt(cHandle,CURLOPT_POSTFIELDS,postfields); + } + if ( hdr0 != NULL && hdr0[0] != 0 ) + { + //printf("HDR0.(%s) HDR1.(%s) HDR2.(%s) HDR3.(%s)\n",hdr0!=0?hdr0:"",hdr1!=0?hdr1:"",hdr2!=0?hdr2:"",hdr3!=0?hdr3:""); + headers = curl_slist_append(headers,hdr0); + if ( hdr1 != 0 && hdr1[0] != 0 ) + headers = curl_slist_append(headers,hdr1); + if ( hdr2 != 0 && hdr2[0] != 0 ) + headers = curl_slist_append(headers,hdr2); + if ( hdr3 != 0 && hdr3[0] != 0 ) + headers = curl_slist_append(headers,hdr3); + } //headers = curl_slist_append(0,"Expect:"); + if ( headers != 0 ) + curl_easy_setopt(cHandle,CURLOPT_HTTPHEADER,headers); + //res = curl_easy_perform(cHandle); + memset(&chunk,0,sizeof(chunk)); + curl_easy_setopt(cHandle,CURLOPT_WRITEFUNCTION,WriteMemoryCallback); + curl_easy_setopt(cHandle,CURLOPT_WRITEDATA,(void *)&chunk); + curl_easy_perform(cHandle); + curl_easy_getinfo(cHandle,CURLINFO_RESPONSE_CODE,&code); + if ( headers != 0 ) + curl_slist_free_all(headers); + if ( code != 200 ) + printf("(%s) server responded with code %ld (%s)\n",url,code,chunk.memory); + return(chunk.memory); +} + +uint16_t _komodo_userpass(char *username, char *password, FILE *fp) +{ + char *rpcuser,*rpcpassword,*str,*ipaddress,line[8192]; uint16_t port = 0; + rpcuser = rpcpassword = 0; + username[0] = password[0] = 0; + while ( fgets(line,sizeof(line),fp) != 0 ) + { + if ( line[0] == '#' ) + continue; + //printf("line.(%s) %p %p\n",line,strstr(line,(char *)"rpcuser"),strstr(line,(char *)"rpcpassword")); + if ( (str= strstr(line,(char *)"rpcuser")) != 0 ) + rpcuser = parse_conf_line(str,(char *)"rpcuser"); + else if ( (str= strstr(line,(char *)"rpcpassword")) != 0 ) + rpcpassword = parse_conf_line(str,(char *)"rpcpassword"); + else if ( (str= strstr(line,(char *)"rpcport")) != 0 ) + { + port = atoi(parse_conf_line(str,(char *)"rpcport")); + //fprintf(stderr,"rpcport.%u in file\n",port); + } + else if ( (str= strstr(line,(char *)"ipaddress")) != 0 ) + { + ipaddress = parse_conf_line(str,(char *)"ipaddress"); + strcpy(IPADDRESS,ipaddress); + } + } + if ( rpcuser != 0 && rpcpassword != 0 ) + { + strcpy(username,rpcuser); + strcpy(password,rpcpassword); + } + //printf("rpcuser.(%s) rpcpassword.(%s) %u ipaddress.%s\n",rpcuser,rpcpassword,port,ipaddress); + if ( rpcuser != 0 ) + free(rpcuser); + if ( rpcpassword != 0 ) + free(rpcpassword); + return(port); +} + +uint16_t komodo_userpass(char *userpass,char *symbol) +{ + FILE *fp; uint16_t port = 0; char fname[512],username[512],password[512],confname[KOMODO_ASSETCHAIN_MAXLEN]; + userpass[0] = 0; + if ( strcmp("KMD",symbol) == 0 ) + { +#ifdef __APPLE__ + sprintf(confname,"Komodo.conf"); +#else + sprintf(confname,"komodo.conf"); +#endif + } + else sprintf(confname,"%s.conf",symbol); + //komodo_statefname(fname,symbol,confname); + if ( (fp= fopen(confname,"rb")) != 0 ) + { + port = _komodo_userpass(username,password,fp); + sprintf(userpass,"%s:%s",username,password); + if ( strcmp(symbol,ASSETCHAINS_SYMBOL) == 0 ) + strcpy(USERPASS,userpass); + fclose(fp); + } + return(port); +} + +#define is_cJSON_True(json) ((json) != 0 && ((json)->type & 0xff) == cJSON_True) + +char *komodo_issuemethod(char *userpass,char *method,char *params,uint16_t port) +{ + //static void *cHandle; + char url[512],*retstr=0,*retstr2=0,postdata[8192]; + if ( params == 0 || params[0] == 0 ) + params = (char *)"[]"; + if ( strlen(params) < sizeof(postdata)-128 ) + { + sprintf(url,(char *)"http://%s:%u",IPADDRESS,port); + sprintf(postdata,"{\"method\":\"%s\",\"params\":%s}",method,params); + //printf("[%s] (%s) postdata.(%s) params.(%s) USERPASS.(%s)\n",ASSETCHAINS_SYMBOL,url,postdata,params,USERPASS); + retstr2 = bitcoind_RPC(&retstr,(char *)"debug",url,userpass,method,params); + //retstr = curl_post(&cHandle,url,USERPASS,postdata,0,0,0,0); + } + return(retstr2); +} + +int32_t issue_games_events(bits256 gametxid,uint32_t eventid,int32_t c) +{ + static FILE *fp; + char params[512],*retstr,str[65]; cJSON *retjson,*resobj; int32_t retval = -1; + if ( fp == 0 ) + fp = fopen("events.log","wb"); + sprintf(params,"[\"events\",\"17\",\"[%%22%08x%%22,%%22%s%%22,%u]\"]",c,bits256_str(str,gametxid),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); +} + +int32_t games_sendrawtransaction(char *rawtx) +{ + char *params,*retstr,*hexstr; cJSON *retjson,*resobj; int32_t retval = -1; + params = (char *)malloc(strlen(rawtx) + 16); + sprintf(params,"[\"%s\"]",rawtx); + if ( (retstr= komodo_issuemethod(USERPASS,(char *)"sendrawtransaction",params,GAMES_PORT)) != 0 ) + { + if ( 0 ) // causes 4th level crash + { + static FILE *fp; + if ( fp == 0 ) + fp = fopen("games.sendlog","wb"); + if ( fp != 0 ) + { + fprintf(fp,"%s\n",retstr); + fflush(fp); + } + } + if ( (retjson= cJSON_Parse(retstr)) != 0 ) + { + if ( (resobj= jobj(retjson,(char *)"result")) != 0 ) + { + if ( (hexstr= jstr(resobj,0)) != 0 && is_hexstr(hexstr,64) == 64 ) + retval = 0; + } + free_json(retjson); + } + + /* log sendrawtx result in file */ + + /* + FILE *debug_file; + debug_file = fopen("tx_debug.log", "a"); + fprintf(debug_file, "%s\n", retstr); + fflush(debug_file); + fclose(debug_file); + */ + + free(retstr); + } + free(params); + return(retval); +} + +int32_t games_progress(struct games_state *rs,int32_t waitflag,uint64_t seed,char *keystrokes,int32_t num) +{ + char cmd[16384],hexstr[16384],params[32768],*retstr,*errstr,*rawtx,*pastkeys,*keys; int32_t i,len,numpastkeys,retflag = -1; cJSON *retjson,*resobj; uint8_t *pastcmp; + if ( rs->guiflag != 0 && Gametxidstr[0] != 0 ) + { + if ( rs->keystrokeshex != 0 ) + { + if ( games_sendrawtransaction(rs->keystrokeshex) == 0 ) + { + if ( waitflag == 0 ) + return(0); + else if ( 0 ) + { + while ( games_sendrawtransaction(rs->keystrokeshex) == 0 ) + { + //fprintf(stderr,"pre-rebroadcast\n"); + sleep(10); + } + } + } + free(rs->keystrokeshex), rs->keystrokeshex = 0; + } + if ( 0 && (pastkeys= games_keystrokesload(&numpastkeys,seed,1)) != 0 ) + { + sprintf(params,"[\"extract\",\"17\",\"[%%22%s%%22]\"]",Gametxidstr); + if ( (retstr= komodo_issuemethod(USERPASS,(char *)"cclib",params,GAMES_PORT)) != 0 ) + { + if ( (retjson= cJSON_Parse(retstr)) != 0 ) + { + if ( (resobj= jobj(retjson,(char *)"result")) != 0 && (keys= jstr(resobj,(char *)"keystrokes")) != 0 ) + { + len = strlen(keys) / 2; + pastcmp = (uint8_t *)malloc(len + 1); + decode_hex(pastcmp,len,keys); + fprintf(stderr,"keystrokes.(%s) vs pastkeys\n",keys); + for (i=0; i> keystrokes.log",ASSETCHAINS_SYMBOL,Gametxidstr,hexstr); + if ( system(cmd) != 0 ) + fprintf(stderr,"error issuing (%s)\n",cmd); + } + else + { + static FILE *fp; + if ( fp == 0 ) + fp = fopen("keystrokes.log","a"); + sprintf(params,"[\"keystrokes\",\"17\",\"[%%22%s%%22,%%22%s%%22]\"]",Gametxidstr,hexstr); + if ( (retstr= komodo_issuemethod(USERPASS,(char *)"cclib",params,GAMES_PORT)) != 0 ) + { + if ( fp != 0 ) + { + fprintf(fp,"%s\n",params); + fprintf(fp,"%s\n",retstr); + fflush(fp); + } + if ( (retjson= cJSON_Parse(retstr)) != 0 ) + { + if ( (resobj= jobj(retjson,(char *)"result")) != 0 && (rawtx= jstr(resobj,(char *)"hex")) != 0 ) + { + if ( rs->keystrokeshex != 0 ) + free(rs->keystrokeshex); + if ( (errstr= jstr(resobj,(char *)"error")) == 0 ) + { + rs->keystrokeshex = (char *)malloc(strlen(rawtx)+1); + strcpy(rs->keystrokeshex,rawtx); + retflag = 1; + } else fprintf(stderr,"error sending keystrokes tx\n"), sleep(1); + //fprintf(stderr,"set keystrokestx <- %s\n",rs->keystrokeshex); + } + free_json(retjson); + } + free(retstr); + } + if ( 0 && waitflag != 0 && rs->keystrokeshex != 0 ) + { + while ( games_sendrawtransaction(rs->keystrokeshex) == 0 ) + { + //fprintf(stderr,"post-rebroadcast\n"); + sleep(3); + } + free(rs->keystrokeshex), rs->keystrokeshex = 0; + } + } + } + return(retflag); +} + +int32_t games_setplayerdata(struct games_state *rs,char *gametxidstr) +{ + char cmd[32768]; int32_t i,n,retval=-1; char params[1024],*filestr=0,*pname,*statusstr,*datastr,fname[128]; long allocsize; cJSON *retjson,*array,*item,*resultjson; + if ( rs->guiflag == 0 ) + return(-1); + if ( gametxidstr == 0 || *gametxidstr == 0 ) + return(retval); + if ( 0 ) + { + sprintf(fname,"%s.gameinfo",gametxidstr); + sprintf(cmd,"./komodo-cli -ac_name=%s cclib gameinfo 17 \\\"[%%22%s%%22]\\\" > %s",ASSETCHAINS_SYMBOL,gametxidstr,fname); + if ( system(cmd) != 0 ) + fprintf(stderr,"error issuing (%s)\n",cmd); + else filestr = (char *)OS_fileptr(&allocsize,fname); + } + else + { + sprintf(params,"[\"gameinfo\",\"17\",\"[%%22%s%%22]\"]",gametxidstr); + filestr = komodo_issuemethod(USERPASS,(char *)"cclib",params,GAMES_PORT); + } + if ( filestr != 0 ) + { + if ( (retjson= cJSON_Parse(filestr)) != 0 && (resultjson= jobj(retjson,(char *)"result")) != 0 ) + { + //fprintf(stderr,"gameinfo.(%s)\n",jprint(resultjson,0)); + if ( (array= jarray(&n,resultjson,(char *)"players")) != 0 ) + { + for (i=0; iP,(int32_t)strlen(datastr)/2,datastr); + fprintf(stderr,"set pname[%s] %s\n",pname==0?"":pname,jprint(item,0)); + rs->restoring = 1; + } + } + } + } + } + free_json(retjson); + } + free(filestr); + } + return(retval); +} + +#ifdef _WIN32 +#ifdef _MSC_VER +__inline int msver(void) { + switch (_MSC_VER) { + case 1500: return 2008; + case 1600: return 2010; + case 1700: return 2012; + case 1800: return 2013; + case 1900: return 2015; + //case 1910: return 2017; + default: return (_MSC_VER / 100); + } +} + +static inline bool is_x64(void) { +#if defined(__x86_64__) || defined(_WIN64) || defined(__aarch64__) + return 1; +#elif defined(__amd64__) || defined(__amd64) || defined(_M_X64) || defined(_M_IA64) + return 1; +#else + return 0; +#endif +} + +#define BUILD_DATE __DATE__ " " __TIME__ +#endif // _WIN32 +#endif // _MSC_VER + +int main(int argc, char **argv) +{ + uint64_t seed; FILE *fp = 0; int32_t i,j,c; char userpass[8192]; +#ifdef _WIN32 +#ifdef _MSC_VER + printf("*** games for Windows [ Build %s ] ***\n", BUILD_DATE); + const char* arch = is_x64() ? "64-bits" : "32-bits"; + printf(" Built with VC++ %d (%ld) %s\n\n", msver(), _MSC_FULL_VER, arch); +#endif +#endif + + for (i=j=0; argv[0][i]!=0&&j payload) -{ - uint256 gametxid; int32_t i,len; char str[67]; uint32_t eventid = 0; - if ( (len= payload.size()) > 36 ) - { - 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 payload); +int32_t games_playerdata_validate(int64_t *cashoutp,uint256 &playertxid,struct CCcontract_info *cp,std::vector playerdata,uint256 gametxid,CPubKey pk); +int32_t games_replay2(uint8_t *newdata,uint64_t seed,char *keystrokes,int32_t num,struct games_player *player,int32_t sleepmillis); +void games_packitemstr(char *packitemstr,struct games_packitem *item); +int64_t games_cashout(struct games_player *P); + CScript games_newgameopret(int64_t buyin,int32_t maxplayers) { @@ -1343,99 +1335,6 @@ UniValue games_extract(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) return(result); } -int64_t games_cashout(struct games_player *P) -{ - int32_t dungeonlevel; int64_t mult=10,cashout = 0; - if ( P->amulet != 0 ) - mult *= 5; - dungeonlevel = P->dungeonlevel; - if ( P->amulet != 0 && dungeonlevel < 26 ) - dungeonlevel = 26; - cashout = (uint64_t)P->gold * P->gold * mult * dungeonlevel; - return(cashout); -} - -int32_t games_playerdata_validate(int64_t *cashoutp,uint256 &playertxid,struct CCcontract_info *cp,std::vector playerdata,uint256 gametxid,CPubKey pk) -{ - static uint32_t good,bad; static uint256 prevgame; - char str[512],*keystrokes,gamesaddr[64],str2[67],fname[64]; int32_t i,dungeonlevel,numkeys; std::vector newdata; uint64_t seed; CPubKey gamespk; struct games_player P; - *cashoutp = 0; - gamespk = GetUnspendable(cp,0); - GetCCaddress1of2(cp,gamesaddr,gamespk,pk); - if ( (keystrokes= games_extractgame(0,str,&numkeys,newdata,seed,playertxid,cp,gametxid,gamesaddr)) != 0 ) - { - free(keystrokes); - sprintf(fname,"%s.%llu.pack",GAMENAME,(long long)seed); - remove(fname); - - for (i=0; i no playerdata, good.%d bad.%d\n",good,bad); - } - *cashoutp = 0; - return(0); - } - } - if ( gametxid != prevgame ) - { - prevgame = gametxid; - bad++; - disp_gamesplayerdata(newdata); - disp_gamesplayerdata(playerdata); - fprintf(stderr,"%s playerdata: gold.%d hp.%d strength.%d/%d level.%d exp.%d dl.%d\n",gametxid.GetHex().c_str(),P.gold,P.hitpoints,P.strength&0xffff,P.strength>>16,P.level,P.experience,P.dungeonlevel); - fprintf(stderr,"newdata[%d] != playerdata[%d], numkeys.%d %s pub.%s playertxid.%s good.%d bad.%d\n",(int32_t)newdata.size(),(int32_t)playerdata.size(),numkeys,gamesaddr,pubkey33_str(str2,(uint8_t *)&pk),playertxid.GetHex().c_str(),good,bad); - } - } - sprintf(fname,"%s.%llu.pack",GAMENAME,(long long)seed); - remove(fname); - //fprintf(stderr,"no keys games_extractgame %s\n",gametxid.GetHex().c_str()); - return(-1); -} - UniValue games_finish(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { //vin0 -> highlander vout from creategame TCBOO @@ -1657,1928 +1556,11 @@ UniValue games_setname(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) return(result); } -bool games_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx) -{ - return(true); -} +#include "tetris.cpp" -int32_t games_replay2(uint8_t *newdata,uint64_t seed,char *keystrokes,int32_t num,struct games_player *player,int32_t sleepmillis) -{ - return(-1); -} +#else // STANDALONE -void games_packitemstr(char *packitemstr,struct games_packitem *item) -{ - sprintf(packitemstr,"not yet"); -} -#else - - -#include -#include -#include -#include -#include -#include -#include - -char USERPASS[8192]; uint16_t GAMES_PORT; -char Gametxidstr[67]; -char *clonestr(char *str); - -#define MAXSTR 1024 -char whoami[MAXSTR]; - -#define SMALLVAL 0.000000000000001 -#define SATOSHIDEN ((uint64_t)100000000L) -#define dstr(x) ((double)(x) / SATOSHIDEN) -#define KOMODO_ASSETCHAIN_MAXLEN 65 -char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN],IPADDRESS[100]; - -#ifndef _BITS256 -#define _BITS256 -union _bits256 { uint8_t bytes[32]; uint16_t ushorts[16]; uint32_t uints[8]; uint64_t ulongs[4]; uint64_t txid; }; -typedef union _bits256 bits256; -#endif - -#ifdef _WIN32 -#ifdef _MSC_VER -int gettimeofday(struct timeval * tp, struct timezone * tzp) -{ - // Note: some broken versions only have 8 trailing zero's, the correct epoch has 9 trailing zero's - static const uint64_t EPOCH = ((uint64_t)116444736000000000ULL); - - SYSTEMTIME system_time; - FILETIME file_time; - uint64_t time; - - GetSystemTime(&system_time); - SystemTimeToFileTime(&system_time, &file_time); - time = ((uint64_t)file_time.dwLowDateTime); - time += ((uint64_t)file_time.dwHighDateTime) << 32; - - tp->tv_sec = (long)((time - EPOCH) / 10000000L); - tp->tv_usec = (long)(system_time.wMilliseconds * 1000); - return 0; -} -#endif // _MSC_VER -#endif - - - -double OS_milliseconds() -{ - struct timeval tv; double millis; -#ifdef __MINGW32__ - mingw_gettimeofday(&tv,NULL); -#else - gettimeofday(&tv,NULL); -#endif - millis = ((double)tv.tv_sec * 1000. + (double)tv.tv_usec / 1000.); - //printf("tv_sec.%ld usec.%d %f\n",tv.tv_sec,tv.tv_usec,millis); - return(millis); -} - -int32_t _unhex(char c) -{ - if ( c >= '0' && c <= '9' ) - return(c - '0'); - else if ( c >= 'a' && c <= 'f' ) - return(c - 'a' + 10); - else if ( c >= 'A' && c <= 'F' ) - return(c - 'A' + 10); - return(-1); -} - -int32_t is_hexstr(char *str,int32_t n) -{ - int32_t i; - if ( str == 0 || str[0] == 0 ) - return(0); - for (i=0; str[i]!=0; i++) - { - if ( n > 0 && i >= n ) - break; - if ( _unhex(str[i]) < 0 ) - break; - } - if ( n == 0 ) - return(i); - return(i == n); -} - -int32_t unhex(char c) -{ - int32_t hex; - if ( (hex= _unhex(c)) < 0 ) - { - //printf("unhex: illegal hexchar.(%c)\n",c); - } - return(hex); -} - -unsigned char _decode_hex(char *hex) { return((unhex(hex[0])<<4) | unhex(hex[1])); } - -int32_t decode_hex(uint8_t *bytes,int32_t n,char *hex) -{ - int32_t adjust,i = 0; - //printf("decode.(%s)\n",hex); - if ( is_hexstr(hex,n) <= 0 ) - { - memset(bytes,0,n); - return(n); - } - if ( hex[n-1] == '\n' || hex[n-1] == '\r' ) - hex[--n] = 0; - if ( n == 0 || (hex[n*2+1] == 0 && hex[n*2] != 0) ) - { - if ( n > 0 ) - { - bytes[0] = unhex(hex[0]); - printf("decode_hex n.%d hex[0] (%c) -> %d hex.(%s) [n*2+1: %d] [n*2: %d %c] len.%ld\n",n,hex[0],bytes[0],hex,hex[n*2+1],hex[n*2],hex[n*2],(long)strlen(hex)); - } - bytes++; - hex++; - adjust = 1; - } else adjust = 0; - if ( n > 0 ) - { - for (i=0; i>4) & 0xf); - hexbytes[i*2 + 1] = hexbyte(message[i] & 0xf); - //printf("i.%d (%02x) [%c%c]\n",i,message[i],hexbytes[i*2],hexbytes[i*2+1]); - } - hexbytes[len*2] = 0; - //printf("len.%ld\n",len*2+1); - return((int32_t)len*2+1); -} - -char *bits256_str(char hexstr[65],bits256 x) -{ - init_hexbytes_noT(hexstr,x.bytes,sizeof(x)); - return(hexstr); -} - -long _stripwhite(char *buf,int accept) -{ - int32_t i,j,c; - if ( buf == 0 || buf[0] == 0 ) - return(0); - for (i=j=0; buf[i]!=0; i++) - { - buf[j] = c = buf[i]; - if ( c == accept || (c != ' ' && c != '\n' && c != '\r' && c != '\t' && c != '\b') ) - j++; - } - buf[j] = 0; - return(j); -} - -char *parse_conf_line(char *line,char *field) -{ - line += strlen(field); - for (; *line!='='&&*line!=0; line++) - break; - if ( *line == 0 ) - return(0); - if ( *line == '=' ) - line++; - while ( line[strlen(line)-1] == '\r' || line[strlen(line)-1] == '\n' || line[strlen(line)-1] == ' ' ) - line[strlen(line)-1] = 0; - //printf("LINE.(%s)\n",line); - _stripwhite(line,0); - return(clonestr(line)); -} - -int32_t safecopy(char *dest,char *src,long len) -{ - int32_t i = -1; - if ( src != 0 && dest != 0 && src != dest ) - { - if ( dest != 0 ) - memset(dest,0,len); - for (i=0; i buflen ) - { - *allocsizep = filesize; - *bufp = buf = (uint8_t *)realloc(buf,(long)*allocsizep+64); - } - rewind(fp); - if ( buf == 0 ) - printf("Null buf ???\n"); - else - { - if ( fread(buf,1,(long)filesize,fp) != (unsigned long)filesize ) - printf("error reading filesize.%ld\n",(long)filesize); - buf[filesize] = 0; - } - fclose(fp); - *lenp = filesize; - //printf("loaded.(%s)\n",buf); - } //else printf("OS_loadfile couldnt load.(%s)\n",fname); - return(buf); -} - -uint8_t *OS_fileptr(long *allocsizep,char *fname) -{ - long filesize = 0; uint8_t *buf = 0; void *retptr; - *allocsizep = 0; - retptr = OS_loadfile(fname,&buf,&filesize,allocsizep); - return((uint8_t *)retptr); -} - -struct MemoryStruct { char *memory; size_t size; }; -struct return_string { char *ptr; size_t len; }; - -// return data from the server -#define CURL_GLOBAL_ALL (CURL_GLOBAL_SSL|CURL_GLOBAL_WIN32) -#define CURL_GLOBAL_SSL (1<<0) -#define CURL_GLOBAL_WIN32 (1<<1) - - -/************************************************************************ - * - * Initialize the string handler so that it is thread safe - * - ************************************************************************/ - -void init_string(struct return_string *s) -{ - s->len = 0; - s->ptr = (char *)calloc(1,s->len+1); - if ( s->ptr == NULL ) - { - fprintf(stderr,"init_string malloc() failed\n"); - exit(-1); - } - s->ptr[0] = '\0'; -} - -/************************************************************************ - * - * Use the "writer" to accumulate text until done - * - ************************************************************************/ - -size_t accumulatebytes(void *ptr,size_t size,size_t nmemb,struct return_string *s) -{ - size_t new_len = s->len + size*nmemb; - s->ptr = (char *)realloc(s->ptr,new_len+1); - if ( s->ptr == NULL ) - { - fprintf(stderr, "accumulate realloc() failed\n"); - exit(-1); - } - memcpy(s->ptr+s->len,ptr,size*nmemb); - s->ptr[new_len] = '\0'; - s->len = new_len; - return(size * nmemb); -} - -/************************************************************************ - * - * return the current system time in milliseconds - * - ************************************************************************/ - -#define EXTRACT_BITCOIND_RESULT // if defined, ensures error is null and returns the "result" field -#ifdef EXTRACT_BITCOIND_RESULT - -/************************************************************************ - * - * perform post processing of the results - * - ************************************************************************/ - -char *post_process_bitcoind_RPC(char *debugstr,char *command,char *rpcstr,char *params) -{ - long i,j,len; char *retstr = 0; cJSON *json,*result,*error; - //printf("<<<<<<<<<<< bitcoind_RPC: %s post_process_bitcoind_RPC.%s.[%s]\n",debugstr,command,rpcstr); - if ( command == 0 || rpcstr == 0 || rpcstr[0] == 0 ) - { - if ( strcmp(command,"signrawtransaction") != 0 ) - printf("<<<<<<<<<<< bitcoind_RPC: %s post_process_bitcoind_RPC.%s.[%s]\n",debugstr,command,rpcstr); - return(rpcstr); - } - json = cJSON_Parse(rpcstr); - if ( json == 0 ) - { - printf("<<<<<<<<<<< bitcoind_RPC: %s post_process_bitcoind_RPC.%s can't parse.(%s) params.(%s)\n",debugstr,command,rpcstr,params); - free(rpcstr); - return(0); - } - result = cJSON_GetObjectItem(json,"result"); - error = cJSON_GetObjectItem(json,"error"); - if ( error != 0 && result != 0 ) - { - if ( (error->type&0xff) == cJSON_NULL && (result->type&0xff) != cJSON_NULL ) - { - retstr = cJSON_Print(result); - len = strlen(retstr); - if ( retstr[0] == '"' && retstr[len-1] == '"' ) - { - for (i=1,j=0; itype&0xff) != cJSON_NULL || (result->type&0xff) != cJSON_NULL ) - { - if ( strcmp(command,"signrawtransaction") != 0 ) - printf("<<<<<<<<<<< bitcoind_RPC: %s post_process_bitcoind_RPC (%s) error.%s\n",debugstr,command,rpcstr); - } - free(rpcstr); - } else retstr = rpcstr; - free_json(json); - //fprintf(stderr,"<<<<<<<<<<< bitcoind_RPC: postprocess returns.(%s)\n",retstr); - return(retstr); -} -#endif - -#ifdef _WIN32 -#ifdef _MSC_VER -#define sleep(x) Sleep(1000*(x)) -#endif -#endif - -/************************************************************************ - * - * perform the query - * - ************************************************************************/ - -char *bitcoind_RPC(char **retstrp,char *debugstr,char *url,char *userpass,char *command,char *params) -{ - static int didinit,count,count2; static double elapsedsum,elapsedsum2; - struct curl_slist *headers = NULL; struct return_string s; CURLcode res; CURL *curl_handle; - char *bracket0,*bracket1,*databuf = 0; long len; int32_t specialcase,numretries; double starttime; - if ( didinit == 0 ) - { - didinit = 1; - curl_global_init(CURL_GLOBAL_ALL); //init the curl session - } - numretries = 0; - if ( debugstr != 0 && strcmp(debugstr,"BTCD") == 0 && command != 0 && strcmp(command,"SuperNET") == 0 ) - specialcase = 1; - else specialcase = 0; - if ( url[0] == 0 ) - strcpy(url,"http://127.0.0.1:7876/nxt"); - if ( specialcase != 0 && 0 ) - printf("<<<<<<<<<<< bitcoind_RPC: debug.(%s) url.(%s) command.(%s) params.(%s)\n",debugstr,url,command,params); -try_again: - if ( retstrp != 0 ) - *retstrp = 0; - starttime = OS_milliseconds(); - curl_handle = curl_easy_init(); - init_string(&s); - headers = curl_slist_append(0,"Expect:"); - - curl_easy_setopt(curl_handle,CURLOPT_USERAGENT,"mozilla/4.0");//"Mozilla/4.0 (compatible; )"); - curl_easy_setopt(curl_handle,CURLOPT_HTTPHEADER, headers); - curl_easy_setopt(curl_handle,CURLOPT_URL, url); - curl_easy_setopt(curl_handle,CURLOPT_WRITEFUNCTION, (void *)accumulatebytes); // send all data to this function - curl_easy_setopt(curl_handle,CURLOPT_WRITEDATA, &s); // we pass our 's' struct to the callback - curl_easy_setopt(curl_handle,CURLOPT_NOSIGNAL, 1L); // supposed to fix "Alarm clock" and long jump crash - curl_easy_setopt(curl_handle,CURLOPT_NOPROGRESS, 1L); // no progress callback - if ( strncmp(url,"https",5) == 0 ) - { - curl_easy_setopt(curl_handle,CURLOPT_SSL_VERIFYPEER,0); - curl_easy_setopt(curl_handle,CURLOPT_SSL_VERIFYHOST,0); - } - if ( userpass != 0 ) - curl_easy_setopt(curl_handle,CURLOPT_USERPWD, userpass); - databuf = 0; - if ( params != 0 ) - { - if ( command != 0 && specialcase == 0 ) - { - len = strlen(params); - if ( len > 0 && params[0] == '[' && params[len-1] == ']' ) { - bracket0 = bracket1 = (char *)""; - } - else - { - bracket0 = (char *)"["; - bracket1 = (char *)"]"; - } - - databuf = (char *)malloc(256 + strlen(command) + strlen(params)); - sprintf(databuf,"{\"id\":\"jl777\",\"method\":\"%s\",\"params\":%s%s%s}",command,bracket0,params,bracket1); - //printf("url.(%s) userpass.(%s) databuf.(%s)\n",url,userpass,databuf); - // - } //else if ( specialcase != 0 ) fprintf(stderr,"databuf.(%s)\n",params); - curl_easy_setopt(curl_handle,CURLOPT_POST,1L); - if ( databuf != 0 ) - curl_easy_setopt(curl_handle,CURLOPT_POSTFIELDS,databuf); - else curl_easy_setopt(curl_handle,CURLOPT_POSTFIELDS,params); - } - //laststart = milliseconds(); - res = curl_easy_perform(curl_handle); - curl_slist_free_all(headers); - curl_easy_cleanup(curl_handle); - if ( databuf != 0 ) // clean up temporary buffer - { - free(databuf); - databuf = 0; - } - if ( res != CURLE_OK ) - { - numretries++; - if ( specialcase != 0 ) - { - printf("<<<<<<<<<<< bitcoind_RPC.(%s): BTCD.%s timeout params.(%s) s.ptr.(%s) err.%d\n",url,command,params,s.ptr,res); - free(s.ptr); - return(0); - } - else if ( numretries >= 1 ) - { - //printf("Maximum number of retries exceeded!\n"); - free(s.ptr); - return(0); - } - if ( (rand() % 1000) == 0 ) - printf( "curl_easy_perform() failed: %s %s.(%s %s), retries: %d\n",curl_easy_strerror(res),debugstr,url,command,numretries); - free(s.ptr); - sleep((1< (%s)\n",params,s.ptr); - count2++; - elapsedsum2 += (OS_milliseconds() - starttime); - if ( (count2 % 10000) == 0) - printf("%d: ave %9.6f | elapsed %.3f millis | NXT calls.(%s) cmd.(%s)\n",count2,elapsedsum2/count2,(double)(OS_milliseconds() - starttime),url,command); - return(s.ptr); - } - } - printf("bitcoind_RPC: impossible case\n"); - free(s.ptr); - return(0); -} - -static size_t WriteMemoryCallback(void *ptr,size_t size,size_t nmemb,void *data) -{ - size_t realsize = (size * nmemb); - struct MemoryStruct *mem = (struct MemoryStruct *)data; - mem->memory = (char *)((ptr != 0) ? realloc(mem->memory,mem->size + realsize + 1) : malloc(mem->size + realsize + 1)); - if ( mem->memory != 0 ) - { - if ( ptr != 0 ) - memcpy(&(mem->memory[mem->size]),ptr,realsize); - mem->size += realsize; - mem->memory[mem->size] = 0; - } - //printf("got %d bytes\n",(int32_t)(size*nmemb)); - return(realsize); -} - -char *curl_post(CURL **cHandlep,char *url,char *userpass,char *postfields,char *hdr0,char *hdr1,char *hdr2,char *hdr3) -{ - struct MemoryStruct chunk; CURL *cHandle; long code; struct curl_slist *headers = 0; - if ( (cHandle= *cHandlep) == NULL ) - *cHandlep = cHandle = curl_easy_init(); - else curl_easy_reset(cHandle); - //#ifdef DEBUG - //curl_easy_setopt(cHandle,CURLOPT_VERBOSE, 1); - //#endif - curl_easy_setopt(cHandle,CURLOPT_USERAGENT,"mozilla/4.0");//"Mozilla/4.0 (compatible; )"); - curl_easy_setopt(cHandle,CURLOPT_SSL_VERIFYPEER,0); - //curl_easy_setopt(cHandle,CURLOPT_SSLVERSION,1); - curl_easy_setopt(cHandle,CURLOPT_URL,url); - curl_easy_setopt(cHandle,CURLOPT_CONNECTTIMEOUT,10); - if ( userpass != 0 && userpass[0] != 0 ) - curl_easy_setopt(cHandle,CURLOPT_USERPWD,userpass); - if ( postfields != 0 && postfields[0] != 0 ) - { - curl_easy_setopt(cHandle,CURLOPT_POST,1); - curl_easy_setopt(cHandle,CURLOPT_POSTFIELDS,postfields); - } - if ( hdr0 != NULL && hdr0[0] != 0 ) - { - //printf("HDR0.(%s) HDR1.(%s) HDR2.(%s) HDR3.(%s)\n",hdr0!=0?hdr0:"",hdr1!=0?hdr1:"",hdr2!=0?hdr2:"",hdr3!=0?hdr3:""); - headers = curl_slist_append(headers,hdr0); - if ( hdr1 != 0 && hdr1[0] != 0 ) - headers = curl_slist_append(headers,hdr1); - if ( hdr2 != 0 && hdr2[0] != 0 ) - headers = curl_slist_append(headers,hdr2); - if ( hdr3 != 0 && hdr3[0] != 0 ) - headers = curl_slist_append(headers,hdr3); - } //headers = curl_slist_append(0,"Expect:"); - if ( headers != 0 ) - curl_easy_setopt(cHandle,CURLOPT_HTTPHEADER,headers); - //res = curl_easy_perform(cHandle); - memset(&chunk,0,sizeof(chunk)); - curl_easy_setopt(cHandle,CURLOPT_WRITEFUNCTION,WriteMemoryCallback); - curl_easy_setopt(cHandle,CURLOPT_WRITEDATA,(void *)&chunk); - curl_easy_perform(cHandle); - curl_easy_getinfo(cHandle,CURLINFO_RESPONSE_CODE,&code); - if ( headers != 0 ) - curl_slist_free_all(headers); - if ( code != 200 ) - printf("(%s) server responded with code %ld (%s)\n",url,code,chunk.memory); - return(chunk.memory); -} - -uint16_t _komodo_userpass(char *username, char *password, FILE *fp) -{ - char *rpcuser,*rpcpassword,*str,*ipaddress,line[8192]; uint16_t port = 0; - rpcuser = rpcpassword = 0; - username[0] = password[0] = 0; - while ( fgets(line,sizeof(line),fp) != 0 ) - { - if ( line[0] == '#' ) - continue; - //printf("line.(%s) %p %p\n",line,strstr(line,(char *)"rpcuser"),strstr(line,(char *)"rpcpassword")); - if ( (str= strstr(line,(char *)"rpcuser")) != 0 ) - rpcuser = parse_conf_line(str,(char *)"rpcuser"); - else if ( (str= strstr(line,(char *)"rpcpassword")) != 0 ) - rpcpassword = parse_conf_line(str,(char *)"rpcpassword"); - else if ( (str= strstr(line,(char *)"rpcport")) != 0 ) - { - port = atoi(parse_conf_line(str,(char *)"rpcport")); - //fprintf(stderr,"rpcport.%u in file\n",port); - } - else if ( (str= strstr(line,(char *)"ipaddress")) != 0 ) - { - ipaddress = parse_conf_line(str,(char *)"ipaddress"); - strcpy(IPADDRESS,ipaddress); - } - } - if ( rpcuser != 0 && rpcpassword != 0 ) - { - strcpy(username,rpcuser); - strcpy(password,rpcpassword); - } - //printf("rpcuser.(%s) rpcpassword.(%s) %u ipaddress.%s\n",rpcuser,rpcpassword,port,ipaddress); - if ( rpcuser != 0 ) - free(rpcuser); - if ( rpcpassword != 0 ) - free(rpcpassword); - return(port); -} - -uint16_t komodo_userpass(char *userpass,char *symbol) -{ - FILE *fp; uint16_t port = 0; char fname[512],username[512],password[512],confname[KOMODO_ASSETCHAIN_MAXLEN]; - userpass[0] = 0; - if ( strcmp("KMD",symbol) == 0 ) - { -#ifdef __APPLE__ - sprintf(confname,"Komodo.conf"); -#else - sprintf(confname,"komodo.conf"); -#endif - } - else sprintf(confname,"%s.conf",symbol); - //komodo_statefname(fname,symbol,confname); - if ( (fp= fopen(confname,"rb")) != 0 ) - { - port = _komodo_userpass(username,password,fp); - sprintf(userpass,"%s:%s",username,password); - if ( strcmp(symbol,ASSETCHAINS_SYMBOL) == 0 ) - strcpy(USERPASS,userpass); - fclose(fp); - } - return(port); -} - -#define is_cJSON_True(json) ((json) != 0 && ((json)->type & 0xff) == cJSON_True) - -char *komodo_issuemethod(char *userpass,char *method,char *params,uint16_t port) -{ - //static void *cHandle; - char url[512],*retstr=0,*retstr2=0,postdata[8192]; - if ( params == 0 || params[0] == 0 ) - params = (char *)"[]"; - if ( strlen(params) < sizeof(postdata)-128 ) - { - sprintf(url,(char *)"http://%s:%u",IPADDRESS,port); - sprintf(postdata,"{\"method\":\"%s\",\"params\":%s}",method,params); - //printf("[%s] (%s) postdata.(%s) params.(%s) USERPASS.(%s)\n",ASSETCHAINS_SYMBOL,url,postdata,params,USERPASS); - retstr2 = bitcoind_RPC(&retstr,(char *)"debug",url,userpass,method,params); - //retstr = curl_post(&cHandle,url,USERPASS,postdata,0,0,0,0); - } - return(retstr2); -} - -int32_t issue_games_events(bits256 gametxid,uint32_t eventid,int32_t c) -{ - static FILE *fp; - char params[512],*retstr,str[65]; cJSON *retjson,*resobj; int32_t retval = -1; - if ( fp == 0 ) - fp = fopen("events.log","wb"); - sprintf(params,"[\"events\",\"17\",\"[%%22%08x%%22,%%22%s%%22,%u]\"]",c,bits256_str(str,gametxid),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); -} - -int32_t games_sendrawtransaction(char *rawtx) -{ - char *params,*retstr,*hexstr; cJSON *retjson,*resobj; int32_t retval = -1; - params = (char *)malloc(strlen(rawtx) + 16); - sprintf(params,"[\"%s\"]",rawtx); - if ( (retstr= komodo_issuemethod(USERPASS,(char *)"sendrawtransaction",params,GAMES_PORT)) != 0 ) - { - if ( 0 ) // causes 4th level crash - { - static FILE *fp; - if ( fp == 0 ) - fp = fopen("games.sendlog","wb"); - if ( fp != 0 ) - { - fprintf(fp,"%s\n",retstr); - fflush(fp); - } - } - if ( (retjson= cJSON_Parse(retstr)) != 0 ) - { - if ( (resobj= jobj(retjson,(char *)"result")) != 0 ) - { - if ( (hexstr= jstr(resobj,0)) != 0 && is_hexstr(hexstr,64) == 64 ) - retval = 0; - } - free_json(retjson); - } - - /* log sendrawtx result in file */ - - /* - FILE *debug_file; - debug_file = fopen("tx_debug.log", "a"); - fprintf(debug_file, "%s\n", retstr); - fflush(debug_file); - fclose(debug_file); - */ - - free(retstr); - } - free(params); - return(retval); -} - -int32_t games_progress(struct games_state *rs,int32_t waitflag,uint64_t seed,char *keystrokes,int32_t num) -{ - char cmd[16384],hexstr[16384],params[32768],*retstr,*errstr,*rawtx,*pastkeys,*keys; int32_t i,len,numpastkeys,retflag = -1; cJSON *retjson,*resobj; uint8_t *pastcmp; - if ( rs->guiflag != 0 && Gametxidstr[0] != 0 ) - { - if ( rs->keystrokeshex != 0 ) - { - if ( games_sendrawtransaction(rs->keystrokeshex) == 0 ) - { - if ( waitflag == 0 ) - return(0); - else if ( 0 ) - { - while ( games_sendrawtransaction(rs->keystrokeshex) == 0 ) - { - //fprintf(stderr,"pre-rebroadcast\n"); - sleep(10); - } - } - } - free(rs->keystrokeshex), rs->keystrokeshex = 0; - } - if ( 0 && (pastkeys= games_keystrokesload(&numpastkeys,seed,1)) != 0 ) - { - sprintf(params,"[\"extract\",\"17\",\"[%%22%s%%22]\"]",Gametxidstr); - if ( (retstr= komodo_issuemethod(USERPASS,(char *)"cclib",params,GAMES_PORT)) != 0 ) - { - if ( (retjson= cJSON_Parse(retstr)) != 0 ) - { - if ( (resobj= jobj(retjson,(char *)"result")) != 0 && (keys= jstr(resobj,(char *)"keystrokes")) != 0 ) - { - len = strlen(keys) / 2; - pastcmp = (uint8_t *)malloc(len + 1); - decode_hex(pastcmp,len,keys); - fprintf(stderr,"keystrokes.(%s) vs pastkeys\n",keys); - for (i=0; i> keystrokes.log",ASSETCHAINS_SYMBOL,Gametxidstr,hexstr); - if ( system(cmd) != 0 ) - fprintf(stderr,"error issuing (%s)\n",cmd); - } - else - { - static FILE *fp; - if ( fp == 0 ) - fp = fopen("keystrokes.log","a"); - sprintf(params,"[\"keystrokes\",\"17\",\"[%%22%s%%22,%%22%s%%22]\"]",Gametxidstr,hexstr); - if ( (retstr= komodo_issuemethod(USERPASS,(char *)"cclib",params,GAMES_PORT)) != 0 ) - { - if ( fp != 0 ) - { - fprintf(fp,"%s\n",params); - fprintf(fp,"%s\n",retstr); - fflush(fp); - } - if ( (retjson= cJSON_Parse(retstr)) != 0 ) - { - if ( (resobj= jobj(retjson,(char *)"result")) != 0 && (rawtx= jstr(resobj,(char *)"hex")) != 0 ) - { - if ( rs->keystrokeshex != 0 ) - free(rs->keystrokeshex); - if ( (errstr= jstr(resobj,(char *)"error")) == 0 ) - { - rs->keystrokeshex = (char *)malloc(strlen(rawtx)+1); - strcpy(rs->keystrokeshex,rawtx); - retflag = 1; - } else fprintf(stderr,"error sending keystrokes tx\n"), sleep(1); - //fprintf(stderr,"set keystrokestx <- %s\n",rs->keystrokeshex); - } - free_json(retjson); - } - free(retstr); - } - if ( 0 && waitflag != 0 && rs->keystrokeshex != 0 ) - { - while ( games_sendrawtransaction(rs->keystrokeshex) == 0 ) - { - //fprintf(stderr,"post-rebroadcast\n"); - sleep(3); - } - free(rs->keystrokeshex), rs->keystrokeshex = 0; - } - } - } - return(retflag); -} - -int32_t games_setplayerdata(struct games_state *rs,char *gametxidstr) -{ - char cmd[32768]; int32_t i,n,retval=-1; char params[1024],*filestr=0,*pname,*statusstr,*datastr,fname[128]; long allocsize; cJSON *retjson,*array,*item,*resultjson; - if ( rs->guiflag == 0 ) - return(-1); - if ( gametxidstr == 0 || *gametxidstr == 0 ) - return(retval); - if ( 0 ) - { - sprintf(fname,"%s.gameinfo",gametxidstr); - sprintf(cmd,"./komodo-cli -ac_name=%s cclib gameinfo 17 \\\"[%%22%s%%22]\\\" > %s",ASSETCHAINS_SYMBOL,gametxidstr,fname); - if ( system(cmd) != 0 ) - fprintf(stderr,"error issuing (%s)\n",cmd); - else filestr = (char *)OS_fileptr(&allocsize,fname); - } - else - { - sprintf(params,"[\"gameinfo\",\"17\",\"[%%22%s%%22]\"]",gametxidstr); - filestr = komodo_issuemethod(USERPASS,(char *)"cclib",params,GAMES_PORT); - } - if ( filestr != 0 ) - { - if ( (retjson= cJSON_Parse(filestr)) != 0 && (resultjson= jobj(retjson,(char *)"result")) != 0 ) - { - //fprintf(stderr,"gameinfo.(%s)\n",jprint(resultjson,0)); - if ( (array= jarray(&n,resultjson,(char *)"players")) != 0 ) - { - for (i=0; iP,(int32_t)strlen(datastr)/2,datastr); - fprintf(stderr,"set pname[%s] %s\n",pname==0?"":pname,jprint(item,0)); - rs->restoring = 1; - } - } - } - } - } - free_json(retjson); - } - free(filestr); - } - return(retval); -} - -#ifdef _WIN32 -#ifdef _MSC_VER -__inline int msver(void) { - switch (_MSC_VER) { - case 1500: return 2008; - case 1600: return 2010; - case 1700: return 2012; - case 1800: return 2013; - case 1900: return 2015; - //case 1910: return 2017; - default: return (_MSC_VER / 100); - } -} - -static inline bool is_x64(void) { -#if defined(__x86_64__) || defined(_WIN64) || defined(__aarch64__) - return 1; -#elif defined(__amd64__) || defined(__amd64) || defined(_M_X64) || defined(_M_IA64) - return 1; -#else - return 0; -#endif -} - -#define BUILD_DATE __DATE__ " " __TIME__ -#endif // _WIN32 -#endif // _MSC_VER - -int main(int argc, char **argv) -{ - uint64_t seed; FILE *fp = 0; int32_t i,j,c; char userpass[8192]; -#ifdef _WIN32 -#ifdef _MSC_VER - printf("*** games for Windows [ Build %s ] ***\n", BUILD_DATE); - const char* arch = is_x64() ? "64-bits" : "32-bits"; - printf(" Built with VC++ %d (%ld) %s\n\n", msver(), _MSC_FULL_VER, arch); -#endif -#endif - - for (i=j=0; argv[0][i]!=0&&j // for FILE -#include // for bool -#include -#include -#include -#include -#include -#include - -#ifdef BUILD_GAMESCC -#include "rogue/cursesd.h" -#else -#include -#endif - -/* - 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; - char *board; - /* - 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; -} 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 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 int GRAVITY_LEVEL[MAX_LEVEL+1]; - -// Data structure manipulation. -void tg_init(tetris_game *obj, int rows, int cols); -tetris_game *tg_create(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(tetris_game *obj, tetris_move move); -void tg_print(tetris_game *obj, FILE *f); - -#endif // TETRIS_H - - -#define MAX(X,Y) ((X) > (Y) ? (X) : (Y)) -#define MIN(X,Y) ((X) < (Y) ? (X) : (Y)) - -/******************************************************************************* - Array Definitions - *******************************************************************************/ - -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}}}, -}; - -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; -} - -/* - Return a random tetromino type. - */ -static int random_tetromino(void) -{ - return rand() % NUM_TETROMINOS; -} - -/* - Create a new falling block and populate the next falling block with a random - one. - */ -static void tg_new_falling(tetris_game *obj) -{ - // Put in a new falling tetromino. - obj->falling = obj->next; - obj->next.typ = random_tetromino(); - 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(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(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(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(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(tetris_game *obj) -{ - tg_remove(obj, obj->falling); - if (obj->stored.typ == -1) { - obj->stored = obj->falling; - tg_new_falling(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(tetris_game *obj, tetris_move move) -{ - switch (move) { - case TM_LEFT: - tg_move(obj, -1); - break; - case TM_RIGHT: - tg_move(obj, 1); - break; - case TM_DROP: - tg_down(obj); - break; - case TM_CLOCK: - tg_rotate(obj, 1); - break; - case TM_COUNTER: - tg_rotate(obj, -1); - break; - case TM_HOLD: - tg_hold(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(tetris_game *obj, tetris_move move) -{ - int lines_cleared; - // Handle gravity. - tg_do_gravity_tick(obj); - - // Handle input. - tg_handle_move(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(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(obj); - tg_new_falling(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(int rows, int cols) -{ - tetris_game *obj = (tetris_game *)malloc(sizeof(tetris_game)); - tg_init(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); -} - -/* - Main tetris game! - */ -#ifdef STANDALONE -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); -} - -int tetris(int argc, char **argv) -{ - tetris_game *tg; - tetris_move move = TM_NONE; - bool running = true; - WINDOW *board, *next, *hold, *score; - int32_t c,skipcount=0; bits256 gametxid; uint32_t eventid = 0; - memset(&gametxid,0,sizeof(gametxid)); - // 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(22, 10); - } - // 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 - - // 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); - int32_t counter = 0; - // Game loop - while (running) { - running = tg_tick(tg, move); - display_board(board, tg); - display_piece(next, tg->next); - display_piece(hold, tg->stored); - display_score(score, tg); - if ( (counter++ % 5) == 0 ) - doupdate(); - sleep_milli(10); - c = getch(); - if ( c != -1 || skipcount == 0x3fff ) - { - if ( skipcount > 0 ) - issue_games_events(gametxid,eventid-skipcount,skipcount | 0x4000); - if ( c != -1 ) - issue_games_events(gametxid,eventid,c); - skipcount = 0; - } else skipcount++; - eventid++; - switch ( c ) - { - case KEY_LEFT: - move = TM_LEFT; - break; - case KEY_RIGHT: - move = TM_RIGHT; - break; - case KEY_UP: - move = TM_CLOCK; - break; - case KEY_DOWN: - 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; - } - } - - // 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; -} - -int32_t games_replay(uint64_t seed,int32_t sleeptime) -{ - return(-1); -} +#include "tetris.c" #endif diff --git a/src/cc/gamescc.h b/src/cc/gamescc.h index 19e5f8d26..07a1f8e65 100644 --- a/src/cc/gamescc.h +++ b/src/cc/gamescc.h @@ -20,7 +20,7 @@ std::string MYCCLIBNAME = (char *)"gamescc"; #define GAMES_MAXKEYSTROKESGAP 60 #define GAMES_MAXPLAYERS 64 #define GAMES_REGISTRATIONSIZE (100 * 10000) -#define GAMES_REGISTRATION 5 +#define GAMES_REGISTRATION 1 #define GAMES_RNGMULT 11109 #define GAMES_RNGOFFSET 13849 @@ -29,7 +29,6 @@ std::string MYCCLIBNAME = (char *)"gamescc"; #define MYCCNAME "games" std::string Games_pname; -#define GAMENAME "sudoku" #define RPC_FUNCS \ { (char *)MYCCNAME, (char *)"rng", (char *)"hash,playerid", 1, 2, ' ', EVAL_GAMES }, \ @@ -104,33 +103,4 @@ if ( cp->evalcode == EVAL_GAMES ) \ } #endif -#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; - char *keystrokes,*keystrokeshex; - uint32_t needflush,replaydone; - int32_t numkeys,ind,num,guiflag,counter,sleeptime,playersize,restoring,lastnum; - FILE *logfp; - struct games_player P; - char buffered[10000]; - uint8_t playerdata[10000]; -}; - -int32_t games_replay2(uint8_t *newdata,uint64_t seed,char *keystrokes,int32_t num,struct games_player *player,int32_t sleepmillis); -void games_packitemstr(char *packitemstr,struct games_packitem *item); - - #endif diff --git a/src/cc/tetris.c b/src/cc/tetris.c new file mode 100644 index 000000000..4ffd27575 --- /dev/null +++ b/src/cc/tetris.c @@ -0,0 +1,758 @@ + +#include "tetris.h" +#include "dapps/dappstd.c" + + +/***************************************************************************/ +/** 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(tetris_game *obj) +{ + // Put in a new falling tetromino. + obj->falling = obj->next; + obj->next.typ = random_tetromino(); + 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(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(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(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(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(tetris_game *obj) +{ + tg_remove(obj, obj->falling); + if (obj->stored.typ == -1) { + obj->stored = obj->falling; + tg_new_falling(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(tetris_game *obj, tetris_move move) +{ + switch (move) { + case TM_LEFT: + tg_move(obj, -1); + break; + case TM_RIGHT: + tg_move(obj, 1); + break; + case TM_DROP: + tg_down(obj); + break; + case TM_CLOCK: + tg_rotate(obj, 1); + break; + case TM_COUNTER: + tg_rotate(obj, -1); + break; + case TM_HOLD: + tg_hold(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(tetris_game *obj, tetris_move move) +{ + int lines_cleared; + // Handle gravity. + tg_do_gravity_tick(obj); + + // Handle input. + tg_handle_move(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(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(obj); + tg_new_falling(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(int rows, int cols) +{ + tetris_game *obj = (tetris_game *)malloc(sizeof(tetris_game)); + tg_init(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); +} + +/* + Main tetris game! + */ +#ifdef STANDALONE +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); +} + +int tetris(int argc, char **argv) +{ + tetris_game *tg; + tetris_move move = TM_NONE; + bool running = true; + WINDOW *board, *next, *hold, *score; + int32_t c,skipcount=0; bits256 gametxid; uint32_t eventid = 0; + memset(&gametxid,0,sizeof(gametxid)); + // 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(22, 10); + } + // 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 + + // 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); + int32_t counter = 0; + // Game loop + while (running) { + running = tg_tick(tg, move); + display_board(board, tg); + display_piece(next, tg->next); + display_piece(hold, tg->stored); + display_score(score, tg); + if ( (counter++ % 5) == 0 ) + doupdate(); + sleep_milli(10); + c = getch(); + if ( c != -1 || skipcount == 0x3fff ) + { + if ( skipcount > 0 ) + issue_games_events(gametxid,eventid-skipcount,skipcount | 0x4000); + if ( c != -1 ) + issue_games_events(gametxid,eventid,c); + skipcount = 0; + } else skipcount++; + eventid++; + switch ( c ) + { + case KEY_LEFT: + move = TM_LEFT; + break; + case KEY_RIGHT: + move = TM_RIGHT; + break; + case KEY_UP: + move = TM_CLOCK; + break; + case KEY_DOWN: + 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; + } + } + + // 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; +} + +int32_t games_replay(uint64_t seed,int32_t sleeptime) +{ + return(-1); +} diff --git a/src/cc/tetris.cpp b/src/cc/tetris.cpp index d6ae473fe..fd833d7b7 100644 --- a/src/cc/tetris.cpp +++ b/src/cc/tetris.cpp @@ -1,939 +1,4 @@ -/***************************************************************************/ -/** 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. - *******************************************************************************/ - - -#ifndef TETRIS_H -#define TETRIS_H - -#include // for FILE -#include // for bool -#include -#include -#include -#include -#include - -#include -#include - -//#include -//#include - - -/* - 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; - char *board; - /* - 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; -} 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 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 int GRAVITY_LEVEL[MAX_LEVEL+1]; - -// Data structure manipulation. -void tg_init(tetris_game *obj, int rows, int cols); -tetris_game *tg_create(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(tetris_game *obj, tetris_move move); -void tg_print(tetris_game *obj, FILE *f); - -#endif // TETRIS_H - - -#define MAX(X,Y) ((X) > (Y) ? (X) : (Y)) -#define MIN(X,Y) ((X) < (Y) ? (X) : (Y)) - -/******************************************************************************* - Array Definitions - *******************************************************************************/ - -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}}}, -}; - -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; -} - -/* - Return a random tetromino type. - */ -static int random_tetromino(void) { - return rand() % NUM_TETROMINOS; -} - -/* - Create a new falling block and populate the next falling block with a random - one. - */ -static void tg_new_falling(tetris_game *obj) -{ - // Put in a new falling tetromino. - obj->falling = obj->next; - obj->next.typ = random_tetromino(); - 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(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(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(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(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(tetris_game *obj) -{ - tg_remove(obj, obj->falling); - if (obj->stored.typ == -1) { - obj->stored = obj->falling; - tg_new_falling(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(tetris_game *obj, tetris_move move) -{ - switch (move) { - case TM_LEFT: - tg_move(obj, -1); - break; - case TM_RIGHT: - tg_move(obj, 1); - break; - case TM_DROP: - tg_down(obj); - break; - case TM_CLOCK: - tg_rotate(obj, 1); - break; - case TM_COUNTER: - tg_rotate(obj, -1); - break; - case TM_HOLD: - tg_hold(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(tetris_game *obj, tetris_move move) -{ - int lines_cleared; - // Handle gravity. - tg_do_gravity_tick(obj); - - // Handle input. - tg_handle_move(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(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(obj); - tg_new_falling(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(int rows, int cols) -{ - tetris_game *obj = (tetris_game *)malloc(sizeof(tetris_game)); - tg_init(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, "Score\n%d\n", tg->points); - wprintw(w, "Level\n%d\n", tg->level); - wprintw(w, "Lines\n%d\n", tg->lines_remaining); - wnoutrefresh(w); -} - -/* - Boss mode! Make it look like you're doing work. - */ -void boss_mode(void) -{ - clear(); - //Mix_PauseMusic(); - printw("user@workstation-312:~/Documents/presentation $ ls -l\n" - "total 528\n" - "drwxr-xr-x 2 user users 4096 Jun 9 17:05 .\n" - "drwxr-xr-x 4 user users 4096 Jun 10 09:52 ..\n" - "-rw-r--r-- 1 user users 88583 Jun 9 14:13 figure1.png\n" - "-rw-r--r-- 1 user users 65357 Jun 9 15:40 figure2.png\n" - "-rw-r--r-- 1 user users 4469 Jun 9 16:17 presentation.aux\n" - "-rw-r--r-- 1 user users 42858 Jun 9 16:17 presentation.log\n" - "-rw-r--r-- 1 user users 2516 Jun 9 16:17 presentation.nav\n" - "-rw-r--r-- 1 user users 183 Jun 9 16:17 presentation.out\n" - "-rw-r--r-- 1 user users 349607 Jun 9 16:17 presentation.pdf\n" - "-rw-r--r-- 1 user users 0 Jun 9 16:17 presentation.snm\n" - "-rw-r--r-- 1 user users 9284 Jun 9 17:05 presentation.tex\n" - "-rw-r--r-- 1 user users 229 Jun 9 16:17 presentation.toc\n" - "\n" - "user@workstation-312:~/Documents/presentation $ "); - echo(); - timeout(-1); - while (getch() != KEY_F(1)); - timeout(0); - noecho(); - clear(); - //Mix_ResumeMusic(); -} - -/* - 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, "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(); - printf("Game saved to \"tetris.save\".\n"); - printf("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); -} - -/* - Main tetris game! - */ -#ifdef STANDALONE - -int main(int argc, char **argv) -{ - tetris_game *tg; - tetris_move move = TM_NONE; - bool running = true; - WINDOW *board, *next, *hold, *score; - //Mix_Music *music; - - // 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(22, 10); - } - - /* Initialize music. - if (SDL_Init(SDL_INIT_AUDIO) < 0) { - fprintf(stderr, "unable to initialize SDL\n"); - exit(EXIT_FAILURE); - } - if (Mix_Init(MIX_INIT_MP3) != MIX_INIT_MP3) { - fprintf(stderr, "unable to initialize SDL_mixer\n"); - exit(EXIT_FAILURE); - } - if (Mix_OpenAudio(MIX_DEFAULT_FREQUENCY, MIX_DEFAULT_FORMAT, 2, 1024) != 0) { - fprintf(stderr, "unable to initialize audio\n"); - exit(EXIT_FAILURE); - } - Mix_AllocateChannels(1); // only need background music - music = Mix_LoadMUS("tetris.mp3"); - if (music) { - Mix_PlayMusic(music, -1); - }*/ - - // 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 - - // 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); - int32_t counter = 0; - // Game loop - while (running) { - running = tg_tick(tg, move); - display_board(board, tg); - display_piece(next, tg->next); - display_piece(hold, tg->stored); - display_score(score, tg); - if ( (counter++ % 5) == 0 ) - doupdate(); - sleep_milli(10); - - switch (getch()) { - case KEY_LEFT: - move = TM_LEFT; - break; - case KEY_RIGHT: - move = TM_RIGHT; - break; - case KEY_UP: - move = TM_CLOCK; - break; - case KEY_DOWN: - 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 'b': - boss_mode(); - move = TM_NONE; - break; - case 's': - save(tg, board); - move = TM_NONE; - break; - case ' ': - move = TM_HOLD; - break; - default: - move = TM_NONE; - } - } - - // Deinitialize NCurses - wclear(stdscr); - endwin(); - - /* Deinitialize Sound - Mix_HaltMusic(); - Mix_FreeMusic(music); - Mix_CloseAudio(); - Mix_Quit();*/ - - // 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; -} -#else - /****************************************************************************** * Copyright © 2014-2019 The SuperNET Developers. * * * @@ -949,973 +14,55 @@ int main(int argc, char **argv) * * ******************************************************************************/ -#include "cJSON.h" -#include "CCinclude.h" +// game specific code for daemon -#define TETRIS_REGISTRATION 5 -#define TETRIS_REGISTRATIONSIZE (100 * 10000) -#define TETRIS_MAXPLAYERS 64 // need to send unused fees back to globalCC address to prevent leeching -#define TETRIS_MAXKEYSTROKESGAP 60 -#define TETRIS_MAXITERATIONS 777 - - -std::string Tetris_pname = ""; - -CScript tetris_newgameopret(int64_t buyin,int32_t maxplayers) +int32_t games_payloadrecv(CPubKey pk,uint32_t timestamp,std::vector payload) { - CScript opret; uint8_t evalcode = EVAL_TETRIS; - opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'G' << buyin << maxplayers); - return(opret); -} - -CScript tetris_registeropret(uint256 gametxid,uint256 playertxid) -{ - CScript opret; uint8_t evalcode = EVAL_TETRIS; - //fprintf(stderr,"opret.(%s %s).R\n",gametxid.GetHex().c_str(),playertxid.GetHex().c_str()); - opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'R' << gametxid << playertxid); - return(opret); -} - -CScript tetris_keystrokesopret(uint256 gametxid,uint256 batontxid,CPubKey pk,std::vectorkeystrokes) -{ - CScript opret; uint8_t evalcode = EVAL_TETRIS; - opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'K' << gametxid << batontxid << pk << keystrokes); - return(opret); -} - -CScript tetris_highlanderopret(uint8_t funcid,uint256 gametxid,int32_t regslot,CPubKey pk,std::vectorplayerdata,std::string pname) -{ - CScript opret; uint8_t evalcode = EVAL_TETRIS; std::string symbol(ASSETCHAINS_SYMBOL); - opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << gametxid << symbol << pname << regslot << pk << playerdata ); - return(opret); -} - -uint8_t tetris_highlanderopretdecode(uint256 &gametxid, uint256 &tokenid, int32_t ®slot, CPubKey &pk, std::vector &playerdata, std::string &symbol, std::string &pname,CScript scriptPubKey) -{ - std::string name, description; std::vector vorigPubkey; - std::vector> oprets, opretsDummy; - std::vector vopretNonfungible, vopret, vopretDummy,origpubkey; - uint8_t e, f,*script; std::vector voutPubkeys; - tokenid = zeroid; - GetOpReturnData(scriptPubKey, vopret); - script = (uint8_t *)vopret.data(); - if ( script[1] == 'c' && (f= DecodeTokenCreateOpRet(scriptPubKey,origpubkey,name,description, oprets)) == 'c' ) + uint256 gametxid; int32_t i,len; char str[67]; uint32_t eventid = 0; + if ( (len= payload.size()) > 36 ) { - GetOpretBlob(oprets, OPRETID_NONFUNGIBLEDATA, vopretNonfungible); - vopret = vopretNonfungible; - } - else if ( script[1] != 'H' && script[1] != 'Q' && (f= DecodeTokenOpRet(scriptPubKey, e, tokenid, voutPubkeys, opretsDummy)) != 0 ) - { - //fprintf(stderr,"decode opret %c tokenid.%s\n",script[1],tokenid.GetHex().c_str()); - GetNonfungibleData(tokenid, vopretNonfungible); //load nonfungible data from the 'tokenbase' tx - vopret = vopretNonfungible; - } - if ( vopret.size() > 2 && E_UNMARSHAL(vopret, ss >> e; ss >> f; ss >> gametxid; ss >> symbol; ss >> pname; ss >> regslot; ss >> pk; ss >> playerdata) != 0 && e == EVAL_TETRIS && (f == 'H' || f == 'Q') ) - { - return(f); - } - fprintf(stderr,"SKIP obsolete: e.%d f.%c game.%s regslot.%d psize.%d\n",e,f,gametxid.GetHex().c_str(),regslot,(int32_t)playerdata.size()); - return(0); -} - -uint8_t tetris_keystrokesopretdecode(uint256 &gametxid,uint256 &batontxid,CPubKey &pk,std::vector &keystrokes,CScript scriptPubKey) -{ - std::vector vopret; uint8_t e,f; - GetOpReturnData(scriptPubKey,vopret); - if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> gametxid; ss >> batontxid; ss >> pk; ss >> keystrokes) != 0 && e == EVAL_TETRIS && f == 'K' ) - { - return(f); - } - return(0); -} - -uint8_t tetris_registeropretdecode(uint256 &gametxid,uint256 &tokenid,uint256 &playertxid,CScript scriptPubKey) -{ - std::string name, description; std::vector vorigPubkey; - std::vector> oprets; - std::vector vopretNonfungible, vopret, vopretDummy,origpubkey; - uint8_t e, f,*script; std::vector voutPubkeys; - tokenid = zeroid; - GetOpReturnData(scriptPubKey, vopret); - script = (uint8_t *)vopret.data(); - if ( script[1] == 'c' && (f= DecodeTokenCreateOpRet(scriptPubKey,origpubkey,name,description,oprets)) == 'c' ) - { - GetOpretBlob(oprets, OPRETID_NONFUNGIBLEDATA, vopretNonfungible); - vopret = vopretNonfungible; - } - else if ( script[1] != 'R' && (f= DecodeTokenOpRet(scriptPubKey, e, tokenid, voutPubkeys, oprets)) != 0 ) - { - GetOpretBlob(oprets, OPRETID_TETRISGAMEDATA, vopretDummy); // blob from non-creation tx opret - vopret = vopretDummy; - } - if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> gametxid; ss >> playertxid) != 0 && e == EVAL_TETRIS && f == 'R' ) - { - return(f); - } - //fprintf(stderr,"e.%d f.%c game.%s playertxid.%s\n",e,f,gametxid.GetHex().c_str(),playertxid.GetHex().c_str()); - return(0); -} - -uint8_t tetris_newgameopreturndecode(int64_t &buyin,int32_t &maxplayers,CScript scriptPubKey) -{ - std::vector vopret; uint8_t e,f; - GetOpReturnData(scriptPubKey,vopret); - if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> buyin; ss >> maxplayers) != 0 && e == EVAL_TETRIS && f == 'G' ) - { - return(f); - } - return(0); -} - -void tetris_univalue(UniValue &result,const char *method,int64_t maxplayers,int64_t buyin) -{ - if ( method != 0 ) - { - result.push_back(Pair("name","tetris")); - result.push_back(Pair("method",method)); - } - if ( maxplayers > 0 ) - result.push_back(Pair("maxplayers",maxplayers)); - if ( buyin >= 0 ) - { - result.push_back(Pair("buyin",ValueFromAmount(buyin))); - if ( buyin == 0 ) - result.push_back(Pair("type","newbie")); - else result.push_back(Pair("type","buyin")); - } -} - -int32_t tetris_iamregistered(int32_t maxplayers,uint256 gametxid,CTransaction tx,char *mytetrisaddr) -{ - int32_t i,vout; uint256 spenttxid,hashBlock; CTransaction spenttx; char destaddr[64]; - for (i=0; i= 0 ) - { - if ( myGetTransaction(spenttxid,spenttx,hashBlock) != 0 && spenttx.vout.size() > 0 ) - { - Getscriptaddress(destaddr,spenttx.vout[0].scriptPubKey); - if ( strcmp(mytetrisaddr,destaddr) == 0 ) - return(1); - //else fprintf(stderr,"myaddr.%s vs %s\n",mytetrisaddr,destaddr); - } //else fprintf(stderr,"cant find spenttxid.%s\n",spenttxid.GetHex().c_str()); - } //else fprintf(stderr,"vout %d is unspent\n",vout); - } - return(0); -} - -int32_t tetris_isvalidgame(struct CCcontract_info *cp,int32_t &gameheight,CTransaction &tx,int64_t &buyin,int32_t &maxplayers,uint256 txid,int32_t unspentv0) -{ - uint256 hashBlock; int32_t i,numvouts; char coinaddr[64]; CPubKey tetrispk; uint64_t txfee = 10000; - buyin = maxplayers = 0; - if ( (txid == zeroid || myGetTransaction(txid,tx,hashBlock) != 0) && (numvouts= tx.vout.size()) > 1 ) - { - if ( txid != zeroid ) - gameheight = komodo_blockheight(hashBlock); - else - { - txid = tx.GetHash(); - //fprintf(stderr,"set txid %s %llu\n",txid.GetHex().c_str(),(long long)CCgettxout(txid,0,1)); - } - if ( IsCClibvout(cp,tx,0,cp->unspendableCCaddr) == txfee && (unspentv0 == 0 || CCgettxout(txid,0,1,0) == txfee) ) - { - if ( tetris_newgameopreturndecode(buyin,maxplayers,tx.vout[numvouts-1].scriptPubKey) == 'G' ) - { - if ( maxplayers < 1 || maxplayers > TETRIS_MAXPLAYERS || buyin < 0 ) - return(-6); - if ( numvouts > 2*maxplayers+1 ) - { - for (i=0; i playerdata,uint256 playertxid,uint256 tokenid,std::string symbol,std::string pname,uint256 gametxid) +int64_t games_cashout(struct games_player *P) { - int32_t i,vout,spentvini,numvouts,n=0; uint256 txid,spenttxid,hashBlock; struct tetris_player P; char packitemstr[512],*datastr=0; UniValue obj(UniValue::VOBJ),a(UniValue::VARR); CTransaction tx; - memset(&P,0,sizeof(P)); - if ( playerdata.size() > 0 ) - { - datastr = (char *)malloc(playerdata.size()*2+1); - for (i=0; i= 0 ) - txid = spenttxid; - else if ( myIsutxo_spentinmempool(spenttxid,spentvini,txid,vout) == 0 || spenttxid == zeroid ) - { - fprintf(stderr,"mempool tracking error %s/v0\n",txid.ToString().c_str()); - break; - } - txid = spenttxid; - vout = 0; - if ( myGetTransaction(txid,tx,hashBlock) != 0 && (numvouts= tx.vout.size()) > 1 ) - { - for (i=0; i TETRIS_MAXITERATIONS ) - break; - } - obj.push_back(Pair("gametxid",gametxid.GetHex())); - if ( txid != playertxid ) - obj.push_back(Pair("batontxid",txid.GetHex())); - obj.push_back(Pair("playertxid",playertxid.GetHex())); - if ( tokenid != zeroid ) - obj.push_back(Pair("tokenid",tokenid.GetHex())); - else obj.push_back(Pair("tokenid",playertxid.GetHex())); - if ( datastr != 0 ) - { - obj.push_back(Pair("data",datastr)); - free(datastr); - } - obj.push_back(Pair("pack",a)); - 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)); - obj.push_back(Pair("chain",symbol)); - obj.push_back(Pair("pname",pname)); - return(obj); + int32_t dungeonlevel; int64_t mult=10,cashout = 0; + if ( P->amulet != 0 ) + mult *= 5; + dungeonlevel = P->dungeonlevel; + if ( P->amulet != 0 && dungeonlevel < 26 ) + dungeonlevel = 26; + cashout = (uint64_t)P->gold * P->gold * mult * dungeonlevel; + return(cashout); } -int32_t tetris_iterateplayer(uint256 ®istertxid,uint256 firsttxid,int32_t firstvout,uint256 lasttxid) // retrace playertxid vins to reach highlander <- this verifies player is valid and tetris_playerdataspend makes sure it can only be used once -{ - uint256 spenttxid,txid = firsttxid; int32_t spentvini,n,vout = firstvout; - registertxid = zeroid; - if ( vout < 0 ) - return(-1); - n = 0; - while ( (spentvini= myIsutxo_spent(spenttxid,txid,vout)) == 0 ) - { - txid = spenttxid; - vout = spentvini; - if ( registertxid == zeroid ) - registertxid = txid; - if ( ++n >= TETRIS_MAXITERATIONS ) - { - fprintf(stderr,"tetris_iterateplayer n.%d, seems something is wrong\n",n); - return(-2); - } - } - if ( txid == lasttxid ) - return(0); - else - { - fprintf(stderr,"firsttxid.%s/v%d -> %s != last.%s\n",firsttxid.ToString().c_str(),firstvout,txid.ToString().c_str(),lasttxid.ToString().c_str()); - return(-1); - } -} - -/* - playertxid is whoever owns the nonfungible satoshi and it might have been bought and sold many times. - highlander is the game winning tx with the player data and is the only place where the unique player data exists - origplayergame is the gametxid that ends up being won by the highlander and they are linked directly as the highlander tx spends gametxid.vout0 - */ - -int32_t tetris_playerdata(struct CCcontract_info *cp,uint256 &origplayergame,uint256 &tokenid,CPubKey &pk,std::vector &playerdata,std::string &symbol,std::string &pname,uint256 playertxid) -{ - uint256 origplayertxid,hashBlock,gametxid,registertxid; CTransaction gametx,playertx,highlandertx; std::vector vopret; uint8_t *script,e,f; int32_t i,regslot,gameheight,numvouts,maxplayers; int64_t buyin; - if ( myGetTransaction(playertxid,playertx,hashBlock) != 0 && (numvouts= playertx.vout.size()) > 0 ) - { - if ( (f= tetris_highlanderopretdecode(gametxid,tokenid,regslot,pk,playerdata,symbol,pname,playertx.vout[numvouts-1].scriptPubKey)) == 'H' || f == 'Q' ) - { - origplayergame = gametxid; - if ( tokenid != zeroid ) - { - playertxid = tokenid; - if ( myGetTransaction(playertxid,playertx,hashBlock) == 0 || (numvouts= playertx.vout.size()) <= 0 ) - { - fprintf(stderr,"couldnt get tokenid.%s\n",playertxid.GetHex().c_str()); - return(-2); - } - } - if ( tetris_isvalidgame(cp,gameheight,gametx,buyin,maxplayers,gametxid,0) == 0 ) - { - //fprintf(stderr,"playertxid.%s got vin.%s/v%d gametxid.%s iterate.%d\n",playertxid.ToString().c_str(),playertx.vin[1].prevout.hash.ToString().c_str(),(int32_t)playertx.vin[1].prevout.n-maxplayers,gametxid.ToString().c_str(),tetris_iterateplayer(registertxid,gametxid,playertx.vin[1].prevout.n-maxplayers,playertxid)); - if ( (tokenid != zeroid || playertx.vin[1].prevout.hash == gametxid) && tetris_iterateplayer(registertxid,gametxid,playertx.vin[1].prevout.n-maxplayers,playertxid) == 0 ) - { - // if registertxid has vin from pk, it can be used - return(0); - } else fprintf(stderr,"hash mismatch or illegal gametxid\n"); - } else fprintf(stderr,"invalid game %s\n",gametxid.GetHex().c_str()); - } //else fprintf(stderr,"invalid player funcid.%c\n",f); - } else fprintf(stderr,"couldnt get playertxid.%s\n",playertxid.GetHex().c_str()); - return(-1); -} - -int32_t tetris_playerdataspend(CMutableTransaction &mtx,uint256 playertxid,int32_t vout,uint256 origplayergame) -{ - int64_t txfee = 10000; CTransaction tx; uint256 hashBlock; - if ( CCgettxout(playertxid,vout,1,0) == 1 ) // not sure if this is enough validation - { - mtx.vin.push_back(CTxIn(playertxid,vout,CScript())); - return(0); - } - else - { - vout = 0; - if ( myGetTransaction(playertxid,tx,hashBlock) != 0 && tx.vout[vout].nValue == 1 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 ) - { - if ( CCgettxout(playertxid,vout,1,0) == 1 ) // not sure if this is enough validation - { - mtx.vin.push_back(CTxIn(playertxid,vout,CScript())); - return(0); - } - } - return(-1); - } -} - -int32_t tetris_findbaton(struct CCcontract_info *cp,uint256 &playertxid,char **keystrokesp,int32_t &numkeys,int32_t ®slot,std::vector &playerdata,uint256 &batontxid,int32_t &batonvout,int64_t &batonvalue,int32_t &batonht,uint256 gametxid,CTransaction gametx,int32_t maxplayers,char *destaddr,int32_t &numplayers,std::string &symbol,std::string &pname) -{ - int32_t i,numvouts,spentvini,n,matches = 0; CPubKey pk; uint256 tid,active,spenttxid,tokenid,hashBlock,txid,origplayergame; CTransaction spenttx,matchtx,batontx; std::vector checkdata; CBlockIndex *pindex; char ccaddr[64],*keystrokes=0; - batonvalue = numkeys = numplayers = batonht = 0; - playertxid = batontxid = zeroid; - for (i=0; i= 0 ) - { - if ( myGetTransaction(spenttxid,spenttx,hashBlock) != 0 && spenttx.vout.size() > 0 ) - { - numplayers++; - Getscriptaddress(ccaddr,spenttx.vout[0].scriptPubKey); - if ( strcmp(destaddr,ccaddr) == 0 ) - { - matches++; - regslot = i; - matchtx = spenttx; - } //else fprintf(stderr,"%d+1 doesnt match %s vs %s\n",i,ccaddr,destaddr); - } //else fprintf(stderr,"%d+1 couldnt find spenttx.%s\n",i,spenttxid.GetHex().c_str()); - } //else fprintf(stderr,"%d+1 unspent\n",i); - } - if ( matches == 1 ) - { - numvouts = matchtx.vout.size(); - //fprintf(stderr,"matchtxid.%s matches.%d numvouts.%d\n",matchtx.GetHash().GetHex().c_str(),matches,numvouts); - if ( tetris_registeropretdecode(txid,tokenid,playertxid,matchtx.vout[numvouts-1].scriptPubKey) == 'R' )//&& txid == gametxid ) - { - //fprintf(stderr,"tokenid.%s txid.%s vs gametxid.%s player.%s\n",tokenid.GetHex().c_str(),txid.GetHex().c_str(),gametxid.GetHex().c_str(),playertxid.GetHex().c_str()); - if ( tokenid != zeroid ) - active = tokenid; - else active = playertxid; - if ( active == zeroid || tetris_playerdata(cp,origplayergame,tid,pk,playerdata,symbol,pname,active) == 0 ) - { - txid = matchtx.GetHash(); - //fprintf(stderr,"scan forward active.%s spenttxid.%s\n",active.GetHex().c_str(),txid.GetHex().c_str()); - n = 0; - while ( CCgettxout(txid,0,1,0) < 0 ) - { - spenttxid = zeroid; - spentvini = -1; - if ( (spentvini= myIsutxo_spent(spenttxid,txid,0)) >= 0 ) - txid = spenttxid; - else - { - if ( myIsutxo_spentinmempool(spenttxid,spentvini,txid,0) == 0 || spenttxid == zeroid ) - { - fprintf(stderr,"mempool tracking error %s/v0\n",txid.ToString().c_str()); - return(-2); - } - } - txid = spenttxid; - //fprintf(stderr,"n.%d next txid.%s/v%d\n",n,txid.GetHex().c_str(),spentvini); - if ( spentvini != 0 ) // game is over? - { - return(0); - } - if ( keystrokesp != 0 && myGetTransaction(spenttxid,spenttx,hashBlock) != 0 && spenttx.vout.size() >= 2 ) - { - uint256 g,b; CPubKey p; std::vector k; - if ( tetris_keystrokesopretdecode(g,b,p,k,spenttx.vout[spenttx.vout.size()-1].scriptPubKey) == 'K' ) - { - keystrokes = (char *)realloc(keystrokes,numkeys + (int32_t)k.size()); - for (i=0; i= TETRIS_MAXITERATIONS ) - { - fprintf(stderr,"tetris_findbaton n.%d, seems something is wrong\n",n); - return(-5); - } - } - //fprintf(stderr,"set baton %s\n",txid.GetHex().c_str()); - batontxid = txid; - batonvout = 0; // not vini - // how to detect timeout, bailedout, highlander - hashBlock = zeroid; - if ( myGetTransaction(batontxid,batontx,hashBlock) != 0 && batontx.vout.size() > 0 ) - { - if ( hashBlock == zeroid ) - batonht = komodo_nextheight(); - else if ( (pindex= komodo_blockindex(hashBlock)) == 0 ) - return(-4); - else batonht = pindex->GetHeight(); - batonvalue = batontx.vout[0].nValue; - //printf("batonht.%d keystrokes[%d]\n",batonht,numkeys); - return(0); - } else fprintf(stderr,"couldnt find baton\n"); - } else fprintf(stderr,"error with playerdata\n"); - } else fprintf(stderr,"findbaton opret error\n"); - } - return(-1); -} - -int32_t tetris_playersalive(int32_t &openslots,int32_t &numplayers,uint256 gametxid,int32_t maxplayers,int32_t gameht,CTransaction gametx) -{ - int32_t i,n,vout,spentvini,registration_open = 0,alive = 0; CTransaction tx; uint256 txid,spenttxid,hashBlock; CBlockIndex *pindex; uint64_t txfee = 10000; - numplayers = openslots = 0; - if ( komodo_nextheight() <= gameht+TETRIS_MAXKEYSTROKESGAP ) - registration_open = 1; - for (i=0; i= 0 ) - txid = spenttxid; - else if ( myIsutxo_spentinmempool(spenttxid,spentvini,txid,vout) == 0 || spenttxid == zeroid ) - { - fprintf(stderr,"mempool tracking error %s/v0\n",txid.ToString().c_str()); - break; - } - txid = spenttxid; - vout = 0; - //fprintf(stderr,"n.%d next txid.%s/v%d\n",n,txid.GetHex().c_str(),spentvini); - if ( spentvini != 0 ) - break; - if ( n++ > TETRIS_MAXITERATIONS ) - break; - } - if ( txid != zeroid ) - { - if ( myGetTransaction(txid,tx,hashBlock) != 0 ) - { - if ( (pindex= komodo_blockindex(hashBlock)) != 0 ) - { - if ( pindex->GetHeight() <= gameht+TETRIS_MAXKEYSTROKESGAP ) - alive++; - } - } - } - } - } - else if ( registration_open != 0 ) - openslots++; - } - //fprintf(stderr,"numalive.%d openslots.%d\n",alive,openslots); - return(alive); -} - -uint64_t tetris_gamefields(UniValue &obj,int64_t maxplayers,int64_t buyin,uint256 gametxid,char *mytetrisaddr) -{ - CBlockIndex *pindex; int32_t ht,openslots,delay,numplayers; uint256 hashBlock; uint64_t seed=0; char cmd[512]; CTransaction tx; - if ( myGetTransaction(gametxid,tx,hashBlock) != 0 && (pindex= komodo_blockindex(hashBlock)) != 0 ) - { - ht = pindex->GetHeight(); - delay = TETRIS_REGISTRATION * (maxplayers > 1); - obj.push_back(Pair("height",ht)); - obj.push_back(Pair("start",ht+delay)); - if ( komodo_nextheight() > ht+delay ) - { - if ( (pindex= komodo_chainactive(ht+delay)) != 0 ) - { - hashBlock = pindex->GetBlockHash(); - obj.push_back(Pair("starthash",hashBlock.ToString())); - memcpy(&seed,&hashBlock,sizeof(seed)); - seed &= (1LL << 62) - 1; - obj.push_back(Pair("seed",(int64_t)seed)); - if ( tetris_iamregistered(maxplayers,gametxid,tx,mytetrisaddr) > 0 ) - sprintf(cmd,"cc/tetris %llu %s",(long long)seed,gametxid.ToString().c_str()); - else sprintf(cmd,"./komodo-cli -ac_name=%s cclib register %d \"[%%22%s%%22]\"",ASSETCHAINS_SYMBOL,EVAL_TETRIS,gametxid.ToString().c_str()); - obj.push_back(Pair("run",cmd)); - } - } - obj.push_back(Pair("alive",tetris_playersalive(openslots,numplayers,gametxid,maxplayers,ht,tx))); - obj.push_back(Pair("openslots",openslots)); - obj.push_back(Pair("numplayers",numplayers)); - } - obj.push_back(Pair("maxplayers",maxplayers)); - obj.push_back(Pair("buyin",ValueFromAmount(buyin))); - return(seed); -} - -void tetris_gameplayerinfo(struct CCcontract_info *cp,UniValue &obj,uint256 gametxid,CTransaction gametx,int32_t vout,int32_t maxplayers,char *mytetrisaddr) -{ - // identify if bailout or quit or timed out - uint256 batontxid,spenttxid,gtxid,ptxid,tokenid,hashBlock,playertxid; CTransaction spenttx,batontx; int32_t numplayers,regslot,numkeys,batonvout,batonht,retval; int64_t batonvalue; std::vector playerdata; char destaddr[64]; std::string symbol,pname; - destaddr[0] = 0; - if ( myIsutxo_spent(spenttxid,gametxid,vout) >= 0 ) - { - if ( myGetTransaction(spenttxid,spenttx,hashBlock) != 0 && spenttx.vout.size() > 0 ) - Getscriptaddress(destaddr,spenttx.vout[0].scriptPubKey); - } - obj.push_back(Pair("slot",(int64_t)vout-1)); - if ( (retval= tetris_findbaton(cp,playertxid,0,numkeys,regslot,playerdata,batontxid,batonvout,batonvalue,batonht,gametxid,gametx,maxplayers,destaddr,numplayers,symbol,pname)) == 0 ) - { - if ( CCgettxout(gametxid,maxplayers+vout,1,0) == 10000 ) - { - if ( myGetTransaction(batontxid,batontx,hashBlock) != 0 && batontx.vout.size() > 1 ) - { - if ( tetris_registeropretdecode(gtxid,tokenid,ptxid,batontx.vout[batontx.vout.size()-1].scriptPubKey) == 'R' && ptxid == playertxid && gtxid == gametxid ) - obj.push_back(Pair("status","registered")); - else obj.push_back(Pair("status","alive")); - } else obj.push_back(Pair("status","error")); - } else obj.push_back(Pair("status","finished")); - obj.push_back(Pair("baton",batontxid.ToString())); - obj.push_back(Pair("tokenid",tokenid.ToString())); - obj.push_back(Pair("batonaddr",destaddr)); - obj.push_back(Pair("ismine",strcmp(mytetrisaddr,destaddr)==0)); - obj.push_back(Pair("batonvout",(int64_t)batonvout)); - obj.push_back(Pair("batonvalue",ValueFromAmount(batonvalue))); - obj.push_back(Pair("batonht",(int64_t)batonht)); - if ( playerdata.size() > 0 ) - obj.push_back(Pair("player",tetris_playerobj(playerdata,playertxid,tokenid,symbol,pname,gametxid))); - } else fprintf(stderr,"findbaton err.%d\n",retval); -} - -int64_t tetris_registrationbaton(CMutableTransaction &mtx,uint256 gametxid,CTransaction gametx,int32_t maxplayers) -{ - int32_t vout,j,r; int64_t nValue; - if ( gametx.vout.size() > maxplayers+1 ) - { - r = rand() % maxplayers; - for (j=0; j 0 ) - { - result.push_back(Pair("hex",rawtx)); - if ( DecodeHexTx(tx,rawtx) != 0 ) - { - if ( broadcastflag != 0 && myAddtomempool(tx) != 0 ) - RelayTransaction(tx); - result.push_back(Pair("txid",tx.GetHash().ToString())); - result.push_back(Pair("result","success")); - } else result.push_back(Pair("error","decode hex")); - } else result.push_back(Pair("error","couldnt finalize CCtx")); - return(result); -} - -UniValue tetris_newgame(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) -{ - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - UniValue result(UniValue::VOBJ); std::string rawtx; CPubKey tetrispk,mypk; char *jsonstr; uint64_t inputsum,change,required,buyin=0; int32_t i,n,maxplayers = 1; - if ( txfee == 0 ) - txfee = 10000; - if ( params != 0 && (n= cJSON_GetArraySize(params)) > 0 ) - { - if ( n > 0 ) - { - maxplayers = juint(jitem(params,0),0); - if ( n > 1 ) - buyin = jdouble(jitem(params,1),0) * COIN + 0.0000000049; - } - } - if ( maxplayers < 1 || maxplayers > TETRIS_MAXPLAYERS ) - return(cclib_error(result,"illegal maxplayers")); - mypk = pubkey2pk(Mypubkey()); - tetrispk = GetUnspendable(cp,0); - tetris_univalue(result,"newgame",maxplayers,buyin); - required = (3*txfee + maxplayers*(TETRIS_REGISTRATIONSIZE+txfee)); - if ( (inputsum= AddCClibInputs(cp,mtx,tetrispk,required,16,cp->unspendableCCaddr)) >= required ) - { - mtx.vout.push_back(MakeCC1vout(cp->evalcode,txfee,tetrispk)); // for highlander TCBOO creation - for (i=0; ievalcode,TETRIS_REGISTRATIONSIZE,tetrispk,tetrispk)); - for (i=0; ievalcode,txfee,tetrispk,tetrispk)); - if ( (change= inputsum - required) >= txfee ) - mtx.vout.push_back(MakeCC1vout(cp->evalcode,change,tetrispk)); - rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,tetris_newgameopret(buyin,maxplayers)); - return(tetris_rawtxresult(result,rawtx,1)); - } - else return(cclib_error(result,"illegal maxplayers")); - return(result); -} - -UniValue tetris_playerinfo(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) -{ - UniValue result(UniValue::VOBJ); std::vector playerdata; uint256 playertxid,tokenid,origplayergame;int32_t n; CPubKey pk; bits256 t; std::string symbol,pname; - result.push_back(Pair("result","success")); - tetris_univalue(result,"playerinfo",-1,-1); - if ( params != 0 && (n= cJSON_GetArraySize(params)) > 0 ) - { - if ( n > 0 ) - { - playertxid = juint256(jitem(params,0)); - if ( tetris_playerdata(cp,origplayergame,tokenid,pk,playerdata,symbol,pname,playertxid) < 0 ) - return(cclib_error(result,"invalid playerdata")); - result.push_back(Pair("player",tetris_playerobj(playerdata,playertxid,tokenid,symbol,pname,origplayergame))); - } else return(cclib_error(result,"no playertxid")); - return(result); - } else return(cclib_error(result,"couldnt reparse params")); -} - -UniValue tetris_register(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) -{ - // vin0 -> TETRIS_REGISTRATIONSIZE 1of2 registration baton from creategame - // vin1 -> optional nonfungible character vout @ - // vin2 -> original creation TCBOO playerdata used - // vin3+ -> buyin - // vout0 -> keystrokes/completion baton - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - UniValue result(UniValue::VOBJ); char destaddr[64],coinaddr[64]; uint256 tokenid,gametxid,origplayergame,playertxid,hashBlock; int32_t err,maxplayers,gameheight,n,numvouts,vout=1; int64_t inputsum,buyin,CCchange=0; CPubKey pk,mypk,tetrispk,burnpk; CTransaction tx,playertx; std::vector playerdata; std::string rawtx,symbol,pname; bits256 t; - - if ( txfee == 0 ) - txfee = 10000; - mypk = pubkey2pk(Mypubkey()); - burnpk = pubkey2pk(ParseHex(CC_BURNPUBKEY)); - tetrispk = GetUnspendable(cp,0); - tetris_univalue(result,"register",-1,-1); - playertxid = tokenid = zeroid; - if ( params != 0 && (n= cJSON_GetArraySize(params)) > 0 ) - { - if ( n > 0 ) - { - gametxid = juint256(jitem(params,0)); - if ( (err= tetris_isvalidgame(cp,gameheight,tx,buyin,maxplayers,gametxid,1)) == 0 ) - { - if ( n > 1 ) - { - playertxid = juint256(jitem(params,1)); - if ( tetris_playerdata(cp,origplayergame,tokenid,pk,playerdata,symbol,pname,playertxid) < 0 ) - return(cclib_error(result,"couldnt extract valid playerdata")); - if ( tokenid != zeroid ) // if it is tokentransfer this will be 0 - vout = 1; - } - if ( komodo_nextheight() > gameheight + TETRIS_MAXKEYSTROKESGAP ) - return(cclib_error(result,"didnt register in time, TETRIS_MAXKEYSTROKESGAP")); - tetris_univalue(result,0,maxplayers,buyin); - GetCCaddress1of2(cp,coinaddr,tetrispk,mypk); - if ( tetris_iamregistered(maxplayers,gametxid,tx,coinaddr) > 0 ) - return(cclib_error(result,"already registered")); - if ( (inputsum= tetris_registrationbaton(mtx,gametxid,tx,maxplayers)) != TETRIS_REGISTRATIONSIZE ) - return(cclib_error(result,"couldnt find available registration baton")); - else if ( playertxid != zeroid && tetris_playerdataspend(mtx,playertxid,vout,origplayergame) < 0 ) - return(cclib_error(result,"couldnt find playerdata to spend")); - else if ( buyin > 0 && AddNormalinputs(mtx,mypk,buyin,64) < buyin ) - return(cclib_error(result,"couldnt find enough normal funds for buyin")); - if ( tokenid != zeroid ) - { - mtx.vin.push_back(CTxIn(tokenid,0)); // spending cc marker as token is burned - char unspendableTokenAddr[64]; uint8_t tokenpriv[32]; struct CCcontract_info *cpTokens, tokensC; - cpTokens = CCinit(&tokensC, EVAL_TOKENS); - CPubKey unspPk = GetUnspendable(cpTokens, tokenpriv); - GetCCaddress(cpTokens, unspendableTokenAddr, unspPk); - CCaddr2set(cp, EVAL_TOKENS, unspPk, tokenpriv, unspendableTokenAddr); - } - mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,buyin + inputsum - txfee,tetrispk,mypk)); - GetCCaddress1of2(cp,destaddr,tetrispk,tetrispk); - CCaddr1of2set(cp,tetrispk,tetrispk,cp->CCpriv,destaddr); - mtx.vout.push_back(MakeTokensCC1vout(cp->evalcode, 1, burnpk)); - - uint8_t e, funcid; uint256 tid; std::vector voutPubkeys, voutPubkeysEmpty; int32_t didtx = 0; - CScript opretRegister = tetris_registeropret(gametxid, playertxid); - if ( playertxid != zeroid ) - { - voutPubkeysEmpty.push_back(burnpk); - if ( myGetTransaction(playertxid,playertx,hashBlock) != 0 ) - { - std::vector> oprets; - if ( (funcid= DecodeTokenOpRet(playertx.vout.back().scriptPubKey, e, tid, voutPubkeys, oprets)) != 0) - { // if token in the opret - didtx = 1; - if ( funcid == 'c' ) - tid = tokenid == zeroid ? playertxid : tokenid; - vscript_t vopretRegister; - GetOpReturnData(opretRegister, vopretRegister); - rawtx = FinalizeCCTx(0, cp, mtx, mypk, txfee, - EncodeTokenOpRet(tid, voutPubkeysEmpty /*=never spent*/, std::make_pair(OPRETID_TETRISGAMEDATA, vopretRegister))); - } - } - } - if ( didtx == 0 ) - rawtx = FinalizeCCTx(0, cp, mtx, mypk, txfee, opretRegister); - - return(tetris_rawtxresult(result,rawtx,1)); - } else return(cclib_error(result,"invalid gametxid")); - } else return(cclib_error(result,"no gametxid")); - } else return(cclib_error(result,"couldnt reparse params")); -} - -UniValue tetris_keystrokes(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) -{ - // vin0 -> baton from registration or previous keystrokes - // vout0 -> new baton - // opret -> user input chars - // being killed should auto broadcast (possible to be suppressed?) - // respawn to be prevented by including timestamps - int32_t nextheight = komodo_nextheight(); - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); - UniValue result(UniValue::VOBJ); CPubKey tetrispk,mypk; uint256 gametxid,playertxid,batontxid; int64_t batonvalue,buyin; std::vector keystrokes,playerdata; int32_t numplayers,regslot,numkeys,batonht,batonvout,n,elapsed,gameheight,maxplayers; CTransaction tx; CTxOut txout; char *keystrokestr,destaddr[64]; std::string rawtx,symbol,pname; bits256 t; uint8_t mypriv[32]; - if ( txfee == 0 ) - txfee = 10000; - tetris_univalue(result,"keystrokes",-1,-1); - if ( params != 0 && (n= cJSON_GetArraySize(params)) == 2 && (keystrokestr= jstr(jitem(params,1),0)) != 0 ) - { - gametxid = juint256(jitem(params,0)); - result.push_back(Pair("gametxid",gametxid.GetHex())); - result.push_back(Pair("keystrokes",keystrokestr)); - keystrokes = ParseHex(keystrokestr); - mypk = pubkey2pk(Mypubkey()); - tetrispk = GetUnspendable(cp,0); - GetCCaddress1of2(cp,destaddr,tetrispk,mypk); - if ( tetris_isvalidgame(cp,gameheight,tx,buyin,maxplayers,gametxid,1) == 0 ) - { - if ( tetris_findbaton(cp,playertxid,0,numkeys,regslot,playerdata,batontxid,batonvout,batonvalue,batonht,gametxid,tx,maxplayers,destaddr,numplayers,symbol,pname) == 0 ) - { - result.push_back(Pair("batontxid",batontxid.GetHex())); - result.push_back(Pair("playertxid",playertxid.GetHex())); - if ( maxplayers == 1 || nextheight <= batonht+TETRIS_MAXKEYSTROKESGAP ) - { - mtx.vin.push_back(CTxIn(batontxid,batonvout,CScript())); //this validates user if pk - mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,batonvalue-txfee,tetrispk,mypk)); - Myprivkey(mypriv); - CCaddr1of2set(cp,tetrispk,mypk,mypriv,destaddr); - rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,tetris_keystrokesopret(gametxid,batontxid,mypk,keystrokes)); - //fprintf(stderr,"KEYSTROKES.(%s)\n",rawtx.c_str()); - return(tetris_rawtxresult(result,rawtx,1)); - } else return(cclib_error(result,"keystrokes tx was too late")); - } else return(cclib_error(result,"couldnt find batontxid")); - } else return(cclib_error(result,"invalid gametxid")); - } else return(cclib_error(result,"couldnt reparse params")); -} - -char *tetris_extractgame(int32_t makefiles,char *str,int32_t *numkeysp,std::vector &newdata,uint64_t &seed,uint256 &playertxid,struct CCcontract_info *cp,uint256 gametxid,char *tetrisaddr) -{ - CPubKey tetrispk; int32_t i,num,retval,maxplayers,gameheight,batonht,batonvout,numplayers,regslot,numkeys,err; std::string symbol,pname; CTransaction gametx; int64_t buyin,batonvalue; char fname[64],*keystrokes = 0; std::vector playerdata; uint256 batontxid; FILE *fp; uint8_t newplayer[10000]; struct tetris_player P,endP; - tetrispk = GetUnspendable(cp,0); - *numkeysp = 0; - seed = 0; - num = numkeys = 0; - playertxid = zeroid; - str[0] = 0; - if ( (err= tetris_isvalidgame(cp,gameheight,gametx,buyin,maxplayers,gametxid,0)) == 0 ) - { - if ( (retval= tetris_findbaton(cp,playertxid,&keystrokes,numkeys,regslot,playerdata,batontxid,batonvout,batonvalue,batonht,gametxid,gametx,maxplayers,tetrisaddr,numplayers,symbol,pname)) == 0 ) - { - UniValue obj; - seed = tetris_gamefields(obj,maxplayers,buyin,gametxid,tetrisaddr); - //fprintf(stderr,"(%s) found baton %s numkeys.%d seed.%llu playerdata.%d playertxid.%s\n",pname.size()!=0?pname.c_str():Tetris_pname.c_str(),batontxid.ToString().c_str(),numkeys,(long long)seed,(int32_t)playerdata.size(),playertxid.GetHex().c_str()); - memset(&P,0,sizeof(P)); - if ( playerdata.size() > 0 ) - { - for (i=0; i no playerdata\n"); - newdata.resize(0); - *numkeysp = numkeys; - return(keystrokes); - /* P.gold = (P.gold * 8) / 10; - if ( keystrokes != 0 ) - { - free(keystrokes); - keystrokes = 0; - *numkeysp = 0; - return(keystrokes); - }*/ - } - else - { - sprintf(str,"$$$gold.%d hp.%d strength.%d/%d level.%d exp.%d dl.%d",endP.gold,endP.hitpoints,endP.strength&0xffff,endP.strength>>16,endP.level,endP.experience,endP.dungeonlevel); - //fprintf(stderr,"%s\n",str); - *numkeysp = numkeys; - return(keystrokes); - } - } else num = 0; - } - else - { - fprintf(stderr,"extractgame: couldnt find baton keystrokes.%p retval.%d\n",keystrokes,retval); - if ( keystrokes != 0 ) - free(keystrokes), keystrokes = 0; - } - } else fprintf(stderr,"extractgame: invalid game\n"); - //fprintf(stderr,"extract %s\n",gametxid.GetHex().c_str()); - return(0); -} - -UniValue tetris_extract(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) -{ - UniValue result(UniValue::VOBJ); CPubKey pk,tetrispk; int32_t i,n,numkeys,flag = 0; uint64_t seed; char str[512],tetrisaddr[64],*pubstr,*hexstr,*keystrokes = 0; std::vector newdata; uint256 gametxid,playertxid; FILE *fp; uint8_t pub33[33]; - pk = pubkey2pk(Mypubkey()); - tetrispk = GetUnspendable(cp,0); - result.push_back(Pair("name","tetris")); - result.push_back(Pair("method","extract")); - tetrisaddr[0] = 0; - if ( params != 0 && (n= cJSON_GetArraySize(params)) > 0 ) - { - if ( n > 0 ) - { - gametxid = juint256(jitem(params,0)); - result.push_back(Pair("gametxid",gametxid.GetHex())); - if ( n == 2 ) - { - if ( (pubstr= jstr(jitem(params,1),0)) != 0 ) - { - if (strlen(pubstr) == 66 ) - { - decode_hex(pub33,33,pubstr); - pk = buf2pk(pub33); - } - else if ( strlen(pubstr) < 36 ) - strcpy(tetrisaddr,pubstr); - } - //fprintf(stderr,"gametxid.%s %s\n",gametxid.GetHex().c_str(),pubstr); - } - if ( tetrisaddr[0] == 0 ) - GetCCaddress1of2(cp,tetrisaddr,tetrispk,pk); - result.push_back(Pair("tetrisaddr",tetrisaddr)); - str[0] = 0; - if ( (keystrokes= tetris_extractgame(1,str,&numkeys,newdata,seed,playertxid,cp,gametxid,tetrisaddr)) != 0 ) - { - result.push_back(Pair("status","success")); - flag = 1; - hexstr = (char *)malloc(numkeys*2 + 1); - for (i=0; i playerdata,uint256 gametxid,CPubKey pk) +int32_t games_playerdata_validate(int64_t *cashoutp,uint256 &playertxid,struct CCcontract_info *cp,std::vector playerdata,uint256 gametxid,CPubKey pk) { static uint32_t good,bad; static uint256 prevgame; - char str[512],*keystrokes,tetrisaddr[64],str2[67],fname[64]; int32_t i,dungeonlevel,numkeys; std::vector newdata; uint64_t seed,mult = 10; CPubKey tetrispk; struct tetris_player P; + char str[512],*keystrokes,gamesaddr[64],str2[67],fname[64]; int32_t i,dungeonlevel,numkeys; std::vector newdata; uint64_t seed; CPubKey gamespk; struct games_player P; *cashoutp = 0; - tetrispk = GetUnspendable(cp,0); - GetCCaddress1of2(cp,tetrisaddr,tetrispk,pk); - //fprintf(stderr,"call extractgame\n"); - if ( (keystrokes= tetris_extractgame(0,str,&numkeys,newdata,seed,playertxid,cp,gametxid,tetrisaddr)) != 0 ) + gamespk = GetUnspendable(cp,0); + GetCCaddress1of2(cp,gamesaddr,gamespk,pk); + if ( (keystrokes= games_extractgame(0,str,&numkeys,newdata,seed,playertxid,cp,gametxid,gamesaddr)) != 0 ) { - //fprintf(stderr,"numkeys.%d tetris_extractgame %s\n",numkeys,gametxid.GetHex().c_str()); free(keystrokes); - sprintf(fname,"tetris.%llu.pack",(long long)seed); + sprintf(fname,"%s.%llu.pack",GAMENAME,(long long)seed); remove(fname); - - //fprintf(stderr,"extracted.(%s)\n",str); - for (i=0; i>16,P.level,P.experience,P.dungeonlevel); - fprintf(stderr,"newdata[%d] != playerdata[%d], numkeys.%d %s pub.%s playertxid.%s good.%d bad.%d\n",(int32_t)newdata.size(),(int32_t)playerdata.size(),numkeys,tetrisaddr,pubkey33_str(str2,(uint8_t *)&pk),playertxid.GetHex().c_str(),good,bad); + fprintf(stderr,"newdata[%d] != playerdata[%d], numkeys.%d %s pub.%s playertxid.%s good.%d bad.%d\n",(int32_t)newdata.size(),(int32_t)playerdata.size(),numkeys,gamesaddr,pubkey33_str(str2,(uint8_t *)&pk),playertxid.GetHex().c_str(),good,bad); } } - sprintf(fname,"tetris.%llu.pack",(long long)seed); + sprintf(fname,"%s.%llu.pack",GAMENAME,(long long)seed); remove(fname); - //fprintf(stderr,"no keys tetris_extractgame %s\n",gametxid.GetHex().c_str()); + //fprintf(stderr,"no keys games_extractgame %s\n",gametxid.GetHex().c_str()); return(-1); } -UniValue tetris_finishgame(uint64_t txfee,struct CCcontract_info *cp,cJSON *params,char *method) +int32_t games_replay2(uint8_t *newdata,uint64_t seed,char *keystrokes,int32_t num,struct games_player *player,int32_t sleepmillis) // replay in daemon { - //vin0 -> highlander vout from creategame TCBOO - //vin1 -> keystrokes baton of completed game, must be last to quit or first to win, only spent registration batons matter. If more than 60 blocks since last keystrokes, it is forfeit - //vins2+ -> rest of unspent registration utxo so all newgame vouts are spent - //vout0 -> nonfungible character with pack @ - //vout1 -> 1% ingame gold and all the buyins - - // detect if last to bailout - // vin0 -> kestrokes baton of completed game with Q - // vout0 -> playerdata marker - // vout0 -> 1% ingame gold - // get any playerdata, get all keystrokes, replay game and compare final state - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - UniValue result(UniValue::VOBJ); std::string rawtx,symbol,pname; CTransaction gametx; uint64_t seed,mult; int64_t buyin,batonvalue,inputsum,cashout,CCchange=0; int32_t i,err,gameheight,tmp,numplayers,regslot,n,num,dungeonlevel,numkeys,maxplayers,batonht,batonvout; char mytetrisaddr[64],*keystrokes = 0; std::vector playerdata,newdata,nodata; uint256 batontxid,playertxid,gametxid; CPubKey mypk,tetrispk; uint8_t player[10000],mypriv[32],funcid; - struct CCcontract_info *cpTokens, tokensC; - - if ( txfee == 0 ) - txfee = 10000; - mypk = pubkey2pk(Mypubkey()); - tetrispk = GetUnspendable(cp,0); - GetCCaddress1of2(cp,mytetrisaddr,tetrispk,mypk); - result.push_back(Pair("name","tetris")); - result.push_back(Pair("method",method)); - result.push_back(Pair("mytetrisaddr",mytetrisaddr)); - if ( strcmp(method,"bailout") == 0 ) - { - funcid = 'Q'; - mult = 10; //100000; - } - else - { - funcid = 'H'; - mult = 20; //200000; - } - if ( params != 0 && (n= cJSON_GetArraySize(params)) > 0 ) - { - if ( n > 0 ) - { - gametxid = juint256(jitem(params,0)); - result.push_back(Pair("gametxid",gametxid.GetHex())); - if ( (err= tetris_isvalidgame(cp,gameheight,gametx,buyin,maxplayers,gametxid,1)) == 0 ) - { - if ( tetris_findbaton(cp,playertxid,&keystrokes,numkeys,regslot,playerdata,batontxid,batonvout,batonvalue,batonht,gametxid,gametx,maxplayers,mytetrisaddr,numplayers,symbol,pname) == 0 ) - { - UniValue obj; struct tetris_player P; - seed = tetris_gamefields(obj,maxplayers,buyin,gametxid,mytetrisaddr); - fprintf(stderr,"(%s) found baton %s numkeys.%d seed.%llu playerdata.%d\n",pname.size()!=0?pname.c_str():Tetris_pname.c_str(),batontxid.ToString().c_str(),numkeys,(long long)seed,(int32_t)playerdata.size()); - memset(&P,0,sizeof(P)); - if ( playerdata.size() > 0 ) - { - for (i=0; i 0 ) - { - newdata.resize(num); - for (i=0; i no playerdata\n"); - newdata.resize(0); - //P.gold = (P.gold * 8) / 10; - } - else - { - cpTokens = CCinit(&tokensC, EVAL_TOKENS); - mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, txfee, GetUnspendable(cpTokens,NULL))); // marker to token cc addr, burnable and validated - mtx.vout.push_back(MakeTokensCC1vout(cp->evalcode,1,mypk)); - if ( P.amulet != 0 ) - mult *= 5; - dungeonlevel = P.dungeonlevel; - if ( P.amulet != 0 && dungeonlevel < 21 ) - dungeonlevel = 21; - cashout = (uint64_t)P.gold * P.gold * mult * dungeonlevel; - fprintf(stderr,"\nextracted $$$gold.%d -> %.8f TETRIS hp.%d strength.%d/%d level.%d exp.%d dl.%d n.%d amulet.%d\n",P.gold,(double)cashout/COIN,P.hitpoints,P.strength&0xffff,P.strength>>16,P.level,P.experience,P.dungeonlevel,n,P.amulet); - if ( funcid == 'H' && maxplayers > 1 ) - { - if ( P.amulet == 0 ) - { - if ( numplayers != maxplayers ) - return(cclib_error(result,"numplayers != maxplayers")); - else if ( tetris_playersalive(tmp,tmp,gametxid,maxplayers,gameheight,gametx) > 1 ) - return(cclib_error(result,"highlander must be a winner or last one standing")); - } - cashout += numplayers * buyin; - } - if ( cashout >= txfee ) - { - if ( (inputsum= AddCClibInputs(cp,mtx,tetrispk,cashout,16,cp->unspendableCCaddr)) > (uint64_t)P.gold*mult ) - CCchange = (inputsum - cashout); - mtx.vout.push_back(CTxOut(cashout,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); - } - } - } - mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange + (batonvalue-3*txfee),tetrispk)); - Myprivkey(mypriv); - CCaddr1of2set(cp,tetrispk,mypk,mypriv,mytetrisaddr); - CScript opret; - if ( pname.size() == 0 ) - pname = Tetris_pname; - if ( newdata.size() == 0 ) - { - opret = tetris_highlanderopret(funcid, gametxid, regslot, mypk, nodata,pname); - rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,opret); - //fprintf(stderr,"nodata finalizetx.(%s)\n",rawtx.c_str()); - } - else - { - opret = tetris_highlanderopret(funcid, gametxid, regslot, mypk, newdata,pname); - char seedstr[32]; - sprintf(seedstr,"%llu",(long long)seed); - std::vector vopretNonfungible; - GetOpReturnData(opret, vopretNonfungible); - rawtx = FinalizeCCTx(0, cp, mtx, mypk, txfee, EncodeTokenCreateOpRet('c', Mypubkey(), std::string(seedstr), gametxid.GetHex(), vopretNonfungible)); - } - return(tetris_rawtxresult(result,rawtx,1)); - } - result.push_back(Pair("result","success")); - } else fprintf(stderr,"illegal game err.%d\n",err); - } else fprintf(stderr,"parameters only n.%d\n",n); - } - return(result); + return(-1); } -UniValue tetris_bailout(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +void games_packitemstr(char *packitemstr,struct games_packitem *item) { - return(tetris_finishgame(txfee,cp,params,"bailout")); + sprintf(packitemstr,"not yet"); } -UniValue tetris_highlander(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +bool games_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx) { - return(tetris_finishgame(txfee,cp,params,"highlander")); -} - -UniValue tetris_gameinfo(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) -{ - UniValue result(UniValue::VOBJ),a(UniValue::VARR); int32_t i,n,gameheight,maxplayers,numvouts; uint256 txid; CTransaction tx; int64_t buyin; uint64_t seed; bits256 t; char mytetrisaddr[64],str[64]; CPubKey mypk,tetrispk; - result.push_back(Pair("name","tetris")); - result.push_back(Pair("method","gameinfo")); - if ( params != 0 && (n= cJSON_GetArraySize(params)) > 0 ) - { - if ( n > 0 ) - { - txid = juint256(jitem(params,0)); - result.push_back(Pair("gametxid",txid.GetHex())); - if ( tetris_isvalidgame(cp,gameheight,tx,buyin,maxplayers,txid,0) == 0 ) - { - result.push_back(Pair("result","success")); - result.push_back(Pair("gameheight",(int64_t)gameheight)); - mypk = pubkey2pk(Mypubkey()); - tetrispk = GetUnspendable(cp,0); - GetCCaddress1of2(cp,mytetrisaddr,tetrispk,mypk); - //fprintf(stderr,"mytetrisaddr.%s\n",mytetrisaddr); - seed = tetris_gamefields(result,maxplayers,buyin,txid,mytetrisaddr); - result.push_back(Pair("seed",(int64_t)seed)); - for (i=0; i > unspentOutputs; - tetrispk = GetUnspendable(cp,0); - GetCCaddress(cp,coinaddr,tetrispk); - SetCCunspents(unspentOutputs,coinaddr); - nextheight = komodo_nextheight(); - for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) - { - txid = it->first.txhash; - vout = (int32_t)it->first.index; - //char str[65]; fprintf(stderr,"%s check %s/v%d %.8f\n",coinaddr,uint256_str(str,txid),vout,(double)it->second.satoshis/COIN); - if ( it->second.satoshis != txfee || vout != 0 ) // reject any that are not highlander markers - continue; - if ( tetris_isvalidgame(cp,gameheight,tx,buyin,maxplayers,txid,1) == 0 && nextheight <= gameheight+TETRIS_MAXKEYSTROKESGAP ) - { - tetris_playersalive(openslots,numplayers,txid,maxplayers,gameheight,tx); - if ( openslots > 0 ) - a.push_back(txid.GetHex()); - } - } - result.push_back(Pair("result","success")); - tetris_univalue(result,"pending",-1,-1); - result.push_back(Pair("pending",a)); - result.push_back(Pair("numpending",(int64_t)a.size())); - return(result); -} - -UniValue tetris_players(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) -{ - UniValue result(UniValue::VOBJ),a(UniValue::VARR); int64_t buyin; uint256 tokenid,gametxid,txid,hashBlock; CTransaction playertx,tx; int32_t maxplayers,vout,numvouts; std::vector playerdata; CPubKey tetrispk,mypk,pk; std::string symbol,pname; char coinaddr[64]; - std::vector > unspentOutputs; - tetrispk = GetUnspendable(cp,0); - mypk = pubkey2pk(Mypubkey()); - GetTokensCCaddress(cp,coinaddr,mypk); - SetCCunspents(unspentOutputs,coinaddr); - tetris_univalue(result,"players",-1,-1); - for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) - { - txid = it->first.txhash; - vout = (int32_t)it->first.index; - //char str[65]; fprintf(stderr,"%s check %s/v%d %.8f\n",coinaddr,uint256_str(str,txid),vout,(double)it->second.satoshis/COIN); - if ( it->second.satoshis != 1 || vout > 1 ) - continue; - if ( tetris_playerdata(cp,gametxid,tokenid,pk,playerdata,symbol,pname,txid) == 0 )//&& pk == mypk ) - { - a.push_back(txid.GetHex()); - //a.push_back(Pair("playerdata",tetris_playerobj(playerdata))); - } - } - result.push_back(Pair("playerdata",a)); - result.push_back(Pair("numplayerdata",(int64_t)a.size())); - return(result); -} - -UniValue tetris_games(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) -{ - UniValue result(UniValue::VOBJ),a(UniValue::VARR),b(UniValue::VARR); uint256 txid,hashBlock,gametxid,tokenid,playertxid; int32_t vout,maxplayers,gameheight,numvouts; CPubKey tetrispk,mypk; char coinaddr[64]; CTransaction tx,gametx; int64_t buyin; - std::vector > addressIndex; - //std::vector > unspentOutputs; - tetrispk = GetUnspendable(cp,0); - mypk = pubkey2pk(Mypubkey()); - GetCCaddress1of2(cp,coinaddr,tetrispk,mypk); - //SetCCunspents(unspentOutputs,coinaddr); - SetCCtxids(addressIndex,coinaddr); - tetris_univalue(result,"games",-1,-1); - for (std::vector >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) - //for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) - { - txid = it->first.txhash; - vout = (int32_t)it->first.index; - //char str[65]; fprintf(stderr,"%s check %s/v%d %.8f\n",coinaddr,uint256_str(str,txid),vout,(double)it->second.satoshis/COIN); - if ( vout == 0 ) - { - if ( myGetTransaction(txid,tx,hashBlock) != 0 && (numvouts= tx.vout.size()) > 1 ) - { - if ( tetris_registeropretdecode(gametxid,tokenid,playertxid,tx.vout[numvouts-1].scriptPubKey) == 'R' ) - { - if ( tetris_isvalidgame(cp,gameheight,gametx,buyin,maxplayers,gametxid,0) == 0 ) - { - if ( CCgettxout(txid,vout,1,0) < 0 ) - b.push_back(gametxid.GetHex()); - else a.push_back(gametxid.GetHex()); - } - } - } - } - } - result.push_back(Pair("pastgames",b)); - result.push_back(Pair("games",a)); - result.push_back(Pair("numgames",(int64_t)(a.size()+b.size()))); - return(result); -} - -UniValue tetris_setname(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) -{ - UniValue result(UniValue::VOBJ); int32_t n; char *namestr = 0; - tetris_univalue(result,"setname",-1,-1); - if ( params != 0 && (n= cJSON_GetArraySize(params)) > 0 ) - { - if ( n > 0 ) - { - if ( (namestr= jstri(params,0)) != 0 ) - { - result.push_back(Pair("result","success")); - result.push_back(Pair("pname",namestr)); - tetris_pname = namestr; - return(result); - } - } - } - result.push_back(Pair("result","error")); - result.push_back(Pair("error","couldnt get name")); - return(result); -} - -bool tetris_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx) -{ - CScript scriptPubKey; std::vector vopret; uint8_t *script,e,f,funcid,tokentx=0; int32_t i,maxplayers,enabled = 0,decoded=0,regslot,ind,err,dispflag,gameheight,score,numvouts; CTransaction vintx,gametx; CPubKey pk; uint256 hashBlock,gametxid,txid,tokenid,batontxid,playertxid,ptxid; int64_t buyin,cashout; std::vector playerdata,keystrokes; std::string symbol,pname; - if ( strcmp(ASSETCHAINS_SYMBOL,"ROGUE") == 0 ) - { - if (height < 21274 ) - return(true); - else if ( height > 50000 ) - enabled = 1; - } else enabled = 1; - if ( (numvouts= tx.vout.size()) > 1 ) - { - txid = tx.GetHash(); - scriptPubKey = tx.vout[numvouts-1].scriptPubKey; - GetOpReturnData(scriptPubKey,vopret); - if ( vopret.size() > 2 ) - { - script = (uint8_t *)vopret.data(); - funcid = script[1]; - if ( (e= script[0]) == EVAL_TOKENS ) - { - tokentx = funcid; - if ( (funcid= tetris_highlanderopretdecode(gametxid,tokenid,regslot,pk,playerdata,symbol,pname,scriptPubKey)) == 0 ) - { - if ( (funcid= tetris_registeropretdecode(gametxid,tokenid,playertxid,scriptPubKey)) == 0 ) - { - fprintf(stderr,"ht.%d couldnt decode tokens opret (%c)\n",height,script[1]); - } else e = EVAL_TETRIS, decoded = 1; - } else e = EVAL_TETRIS, decoded = 1; - } - if ( e == EVAL_TETRIS ) - { - //fprintf(stderr,"ht.%d tetris.(%c)\n",height,script[1]); - if ( decoded == 0 ) - { - switch ( funcid ) - { - case 'G': // seems just need to make sure no vout abuse is left to do - gametx = tx; - gametxid = tx.GetHash(); - gameheight = height; - if ( (err= tetris_isvalidgame(cp,gameheight,gametx,buyin,maxplayers,zeroid,0)) != 0 ) - { - fprintf(stderr,"height.%d %s tetris_isvalidgame error.%d\n",height,gametxid.GetHex().c_str(),err); - return eval->Invalid("invalid gametxid"); - } - //fprintf(stderr,"height.%d %s tetris_isvalidgame\n",height,gametxid.GetHex().c_str()); - return(true); - break; - case 'R': - if ( (funcid= tetris_registeropretdecode(gametxid,tokenid,playertxid,scriptPubKey)) != 'R' ) - { - return eval->Invalid("couldnt decode register opret"); - } - // baton is created - // validation is done below - break; - case 'K': - if ( (funcid= tetris_keystrokesopretdecode(gametxid,batontxid,pk,keystrokes,scriptPubKey)) != 'K' ) - { - return eval->Invalid("couldnt decode keystrokes opret"); - } - // spending the baton proves it is the user if the pk is the signer - return(true); - break; - case 'H': case 'Q': - // done in the next switch statement as there are some H/Q tx with playerdata which would skip this section - break; - default: - return eval->Invalid("illegal tetris non-decoded funcid"); - break; - } - } - switch ( funcid ) - { - case 'R': // register - // verify vout amounts are as they should be and no vins that shouldnt be - return(true); - case 'H': // win - case 'Q': // bailout - if ( (f= tetris_highlanderopretdecode(gametxid,tokenid,regslot,pk,playerdata,symbol,pname,scriptPubKey)) != funcid ) - { - //fprintf(stderr,"height.%d couldnt decode H/Q opret\n",height); - //if ( height > 20000 ) - return eval->Invalid("couldnt decode H/Q opret"); - } - // verify pk belongs to this tx - if ( tokentx == 'c' && playerdata.size() > 0 ) - { - static char laststr[512]; char cashstr[512]; - if ( tetris_playerdata_validate(&cashout,ptxid,cp,playerdata,gametxid,pk) < 0 ) - { - sprintf(cashstr,"tokentx.(%c) decoded.%d ht.%d gametxid.%s player.%s invalid playerdata[%d]\n",tokentx,decoded,height,gametxid.GetHex().c_str(),ptxid.GetHex().c_str(),(int32_t)playerdata.size()); - if ( strcmp(laststr,cashstr) != 0 ) - { - strcpy(laststr,cashstr); - fprintf(stderr,"%s\n",cashstr); - } - if ( enabled != 0 ) - return eval->Invalid("mismatched playerdata"); - } - if ( funcid == 'H' ) - cashout *= 2; - sprintf(cashstr,"tokentx.(%c) decoded.%d ht.%d txid.%s %.8f vs vout2 %.8f",tokentx,decoded,height,txid.GetHex().c_str(),(double)cashout/COIN,(double)tx.vout[2].nValue/COIN); - if ( strcmp(laststr,cashstr) != 0 ) - { - strcpy(laststr,cashstr); - fprintf(stderr,"%s\n",cashstr); - } - if ( enabled != 0 && tx.vout[2].nValue != cashout ) - return eval->Invalid("mismatched cashout amount"); - } - if ( funcid == 'Q' ) - { - // verify vin/vout - } - else // 'H' - { - // verify vin/vout and proper payouts - } - return(true); - break; - default: - return eval->Invalid("illegal tetris funcid"); - break; - } - } else return eval->Invalid("illegal evalcode"); - } else return eval->Invalid("opret too small"); - } else return eval->Invalid("not enough vouts"); return(true); } -#endif diff --git a/src/cc/tetris.h b/src/cc/tetris.h new file mode 100644 index 000000000..f032bf2da --- /dev/null +++ b/src/cc/tetris.h @@ -0,0 +1,197 @@ +/****************************************************************************** + * 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. * + * * + ******************************************************************************/ + +#ifndef H_TETRIS_H +#define H_TETRIS_H + +#define GAMENAME "tetris" +#define GAMEMAIN tetris +#define CHAINNAME "GTEST" + +#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; + char *keystrokes,*keystrokeshex; + uint32_t needflush,replaydone; + int32_t numkeys,ind,num,guiflag,counter,sleeptime,playersize,restoring,lastnum; + FILE *logfp; + struct games_player P; + char buffered[10000]; + uint8_t playerdata[10000]; +}; + + +/***************************************************************************/ +/** 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; + char *board; + /* + 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; +} 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 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 int GRAVITY_LEVEL[MAX_LEVEL+1]; + +// Data structure manipulation. +void tg_init(tetris_game *obj, int rows, int cols); +tetris_game *tg_create(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(tetris_game *obj, tetris_move move); +void tg_print(tetris_game *obj, FILE *f); + +#endif +