From 9e529f121f5e19720e14f77cce65aed9ecf0ecf7 Mon Sep 17 00:00:00 2001 From: Wohlstand Date: Wed, 8 Nov 2017 00:24:04 +0300 Subject: Fixed Windows MSVC build (adlmidi2 and gen_adldata) --- utils/adlmidi-2/midiplay.cc | 88 +- utils/adlmidi-2/puzzlegame.hpp | 1704 +++++++++++++++--------------- utils/gen_adldata/file_formats/load_jv.h | 2 +- utils/gen_adldata/ini/ini_processing.cpp | 4 +- utils/gen_adldata/measurer.cpp | 4 +- utils/gen_adldata/progs_cache.h | 1 + 6 files changed, 913 insertions(+), 890 deletions(-) (limited to 'utils') diff --git a/utils/adlmidi-2/midiplay.cc b/utils/adlmidi-2/midiplay.cc index 8ede11b..ddfa030 100644 --- a/utils/adlmidi-2/midiplay.cc +++ b/utils/adlmidi-2/midiplay.cc @@ -11,7 +11,9 @@ #include #include #include +#ifndef _WIN32 #include +#endif #include #include #include // vector @@ -20,22 +22,30 @@ #include +#ifndef _WIN32 #define SUPPORT_VIDEO_OUTPUT #define SUPPORT_PUZZLE_GAME +#endif -#ifdef __WIN32__ +#if !defined(_WIN32) || !defined(_MSC_VER) +#define ATTRIBUTE_FORMAT_PRINTF(x, y) __attribute__((format(printf, x, y))) +#else +#define ATTRIBUTE_FORMAT_PRINTF(x, y) +#endif + +#ifdef _WIN32 # include # define WIN32_LEAN_AND_MEAN +# define NOMINMAX //To don't damage std::min and std::max # include # include #endif -#if defined(__WIN32__) || defined(__DJGPP__) +#if defined(_WIN32) || defined(__DJGPP__) typedef unsigned char Uint8; typedef unsigned short Uint16; typedef unsigned Uint32; #else - #define SDL_MAIN_HANDLED #include @@ -69,7 +79,7 @@ public: #include #define BIOStimer _farpeekl(_dos_ds, 0x46C) static const unsigned NewTimerFreq = 209; -#elif !defined(__WIN32__) || defined(__CYGWIN__) +#elif !defined(_WIN32) || defined(__CYGWIN__) # include # include # include @@ -118,7 +128,7 @@ static unsigned WinHeight() return result; } -#if (!defined(__WIN32__) || defined(__CYGWIN__)) && defined(TIOCGWINSZ) +#if (!defined(_WIN32) || defined(__CYGWIN__)) && defined(TIOCGWINSZ) extern "C" { static void SigWinchHandler(int); } @@ -141,19 +151,19 @@ static void GuessInitialWindowHeight() static class Input { - #ifdef __WIN32__ + #ifdef _WIN32 void *inhandle; #endif - #if (!defined(__WIN32__) || defined(__CYGWIN__)) && !defined(__DJGPP__) + #if (!defined(_WIN32) || defined(__CYGWIN__)) && !defined(__DJGPP__) struct termio back; #endif public: Input() { - #ifdef __WIN32__ + #ifdef _WIN32 inhandle = GetStdHandle(STD_INPUT_HANDLE); #endif - #if (!defined(__WIN32__) || defined(__CYGWIN__)) && !defined(__DJGPP__) + #if (!defined(_WIN32) || defined(__CYGWIN__)) && !defined(__DJGPP__) ioctl(0, TCGETA, &back); struct termio term = back; term.c_lflag &= ~(ICANON | ECHO); @@ -164,7 +174,7 @@ public: } ~Input() { - #if (!defined(__WIN32__) || defined(__CYGWIN__)) && !defined(__DJGPP__) + #if (!defined(_WIN32) || defined(__CYGWIN__)) && !defined(__DJGPP__) if(ioctl(0, TCSETA, &back) < 0) fcntl(0, F_SETFL, fcntl(0, F_GETFL) & ~ O_NONBLOCK); #endif @@ -179,7 +189,7 @@ public: return c ? c : getch(); } #endif - #ifdef __WIN32__ + #ifdef _WIN32 DWORD nread = 0; INPUT_RECORD inbuf[1]; while(PeekConsoleInput(inhandle, inbuf, sizeof(inbuf) / sizeof(*inbuf), &nread) && nread) @@ -195,7 +205,7 @@ public: } } #endif - #if (!defined(__WIN32__) || defined(__CYGWIN__)) && !defined(__DJGPP__) + #if (!defined(_WIN32) || defined(__CYGWIN__)) && !defined(__DJGPP__) char c = 0; if(read(0, &c, 1) == 1) return c; #endif @@ -248,7 +258,7 @@ public: bool DirtyCells[TxWidth * TxHeight] = {false}; unsigned NDirty = 0; #endif - #ifdef __WIN32__ + #ifdef _WIN32 void *handle; #endif int x, y, color, txtline, maxy; @@ -268,7 +278,7 @@ public: maxy(0), cursor_visible(true) { GuessInitialWindowHeight(); - #ifdef __WIN32__ + #ifdef _WIN32 handle = GetStdHandle(STD_OUTPUT_HANDLE); GotoXY(41, 13); CONSOLE_SCREEN_BUFFER_INFO tmp; @@ -285,7 +295,7 @@ public: //SetConsoleScreenBufferSize(handle,size); } #endif - #if (!defined(__WIN32__) || defined(__CYGWIN__)) && defined(TIOCGWINSZ) + #if (!defined(_WIN32) || defined(__CYGWIN__)) && defined(TIOCGWINSZ) std::signal(SIGWINCH, SigWinchHandler); #endif #ifdef __DJGPP__ @@ -294,7 +304,9 @@ public: std::memset(slots, '.', sizeof(slots)); std::memset(background, '.', sizeof(background)); std::memset(backgroundcolor, 1, sizeof(backgroundcolor)); + #ifndef _WIN32 setbuffer(stderr, stderr_buffer, sizeof(stderr_buffer)); + #endif RawPrn("\r"); // Ensure cursor is at the x=0 we imagine it being Print(0, 7, true, "Hit Ctrl-C to quit"); } @@ -302,7 +314,7 @@ public: { if(!cursor_visible) return; cursor_visible = false; - #ifdef __WIN32__ + #ifdef _WIN32 if(handle) { const CONSOLE_CURSOR_INFO info = {100, false}; @@ -328,7 +340,7 @@ public: cursor_visible = true; GotoXY(0, maxy); Color(7); - #ifdef __WIN32__ + #ifdef _WIN32 if(handle) { const CONSOLE_CURSOR_INFO info = {100, true}; @@ -422,7 +434,7 @@ public: #endif void PutC(char c) { - #ifdef __WIN32__ + #ifdef _WIN32 if(handle) WriteConsole(handle, &c, 1, 0, 0); else #endif @@ -440,13 +452,16 @@ public: #ifdef __DJGPP__ # define RawPrn cprintf #else - static void RawPrn(const char *fmt, ...) __attribute__((format(printf, 1, 2))) + static void RawPrn(const char *fmt, ...) ATTRIBUTE_FORMAT_PRINTF(1, 2) { // Note: should essentially match PutC, except without updates to x va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); + #ifdef _WIN32 + fflush(stderr); + #endif } #endif @@ -487,7 +502,7 @@ public: return nchars; } - int Print(unsigned beginx, unsigned color, bool ln, const char *fmt, ...) __attribute__((format(printf, 5, 6))) + int Print(unsigned beginx, unsigned color, bool ln, const char *fmt, ...) ATTRIBUTE_FORMAT_PRINTF(5, 6) { va_list ap; va_start(ap, fmt); @@ -495,7 +510,7 @@ public: va_end(ap); return r; } - int PrintLn(const char *fmt, ...) __attribute__((format(printf, 2, 3))) + int PrintLn(const char *fmt, ...) ATTRIBUTE_FORMAT_PRINTF(2, 3) { va_list ap; va_start(ap, fmt); @@ -608,7 +623,7 @@ public: y += 1; x = 0; } - #ifdef __WIN32__ + #ifdef _WIN32 if(handle) { CONSOLE_SCREEN_BUFFER_INFO tmp; @@ -656,7 +671,7 @@ public: { if(color != newcolor) { - #ifdef __WIN32__ + #ifdef _WIN32 if(handle) SetConsoleTextAttribute(handle, newcolor); else @@ -863,7 +878,7 @@ static struct MyReverbData } } reverb_data; -#ifdef __WIN32__ +#ifdef _WIN32 namespace WindowsAudio { static const unsigned BUFFER_COUNT = 16; @@ -1095,7 +1110,7 @@ static void SendStereoAudio(unsigned long count, short *samples) //static unsigned counter = 0; if(++counter < 8000) return; - #if defined(__WIN32__) && 0 + #if defined(_WIN32) && 0 // Cheat on dosbox recording: easier on the cpu load. { count *= 2; @@ -1107,7 +1122,7 @@ static void SendStereoAudio(unsigned long count, short *samples) } #endif - #ifdef __WIN32__ + #ifdef _WIN32 std::vector AudioBuffer(count * 2); const size_t pos = 0; #else @@ -1217,6 +1232,7 @@ static void SendStereoAudio(unsigned long count, short *samples) // raise(SIGINT); } + #ifdef SUPPORT_VIDEO_OUTPUT if(WriteVideoFile) { static constexpr unsigned framerate = 15; @@ -1258,7 +1274,9 @@ static void SendStereoAudio(unsigned long count, short *samples) } } } - #ifndef __WIN32__ + #endif + + #ifndef _WIN32 AudioBuffer_lock.Unlock(); #else if(!WritePCMfile) @@ -1445,7 +1463,7 @@ static void SendStereoAudio(unsigned long count, short *samples) // NextGM(+1); // break; // case 3: -// #if !((!defined(__WIN32__) || defined(__CYGWIN__)) && !defined(__DJGPP__)) +// #if !((!defined(_WIN32) || defined(__CYGWIN__)) && !defined(__DJGPP__)) // case 27: // #endif // return false; @@ -1468,7 +1486,7 @@ static void TidyupAndExit(int) raise(SIGINT); } -#ifdef __WIN32__ +#ifdef _WIN32 /* Parse a command line buffer into arguments */ static void UnEscapeQuotes(char *arg) { @@ -1589,6 +1607,7 @@ int main(int argc, char **argv) // The lag between visual content and audio content equals // the sum of these two buffers. + #ifndef _WIN32 WritingToTTY = isatty(STDOUT_FILENO); if(WritingToTTY) { @@ -1600,6 +1619,9 @@ int main(int argc, char **argv) #endif ); } + #else + WritingToTTY = true; + #endif if(WritingToTTY) { UI.Print(0, 3, true, "(C) -- http://iki.fi/bisqwit/source/adlmidi.html"); @@ -1726,7 +1748,7 @@ int main(int argc, char **argv) #ifndef __DJGPP__ - #ifndef __WIN32__ + #ifndef _WIN32 static SDL_AudioSpec spec, obtained; spec.freq = PCM_RATE; spec.format = AUDIO_S16SYS; @@ -1861,7 +1883,7 @@ int main(int argc, char **argv) //const double maxdelay = MaxSamplesAtTime / (double)PCM_RATE; - #ifdef __WIN32 + #ifdef _WIN32 WindowsAudio::Open(PCM_RATE, 2, 16); #else SDL_PauseAudio(0); @@ -1933,7 +1955,7 @@ int main(int argc, char **argv) //fprintf(stderr, "Enter: %u (%.2f ms)\n", (unsigned)AudioBuffer.size(), // AudioBuffer.size() * .5e3 / obtained.freq); - #ifndef __WIN32__ + #ifndef _WIN32 const SDL_AudioSpec &spec_ = (WritePCMfile ? spec : obtained); for(unsigned grant = 0; AudioBuffer.size() > spec_.samples + (spec_.freq * 2) * OurHeadRoomLength; ++grant) { @@ -2007,7 +2029,7 @@ int main(int argc, char **argv) #else - #ifdef __WIN32__ + #ifdef _WIN32 WindowsAudio::Close(); #else SDL_CloseAudio(); diff --git a/utils/adlmidi-2/puzzlegame.hpp b/utils/adlmidi-2/puzzlegame.hpp index e003f6b..551d4bb 100755 --- a/utils/adlmidi-2/puzzlegame.hpp +++ b/utils/adlmidi-2/puzzlegame.hpp @@ -1,853 +1,853 @@ -/* A block puzzle game. - * Most of the source code comes from https://www.youtube.com/watch?v=V65mtR08fH0 - * Copyright (c) 2012 Joel Yliluoma (http://iki.fi/bisqwit/) - * License: MIT - */ - -// Standard C++ includes: -#include // for std::puts -#include // for std::rand -#include // for std::memcpy -#include // for std::abs, std::max, std::random_shuffle -#include // for std::pair -#include // for std::vector -#include -#include - -// Coroutine library. With thanks to Simon Tatham. -#define ccrVars struct ccrR { int p; ccrR() : p(0) { } } ccrL -#define ccrBegin(x) auto& ccrC=(x); switch(ccrC.ccrL.p) { case 0:; -#define ccrReturn(z) do { ccrC.ccrL.p=__LINE__; return(z); case __LINE__:; } while(0) -#define ccrFinish(z) } ccrC.ccrL.p=0; return(z) - -namespace ADLMIDI_PuzzleGame -{ - // Field & rendering definitions - const auto Width = 18, Height = 25; // Width and height, including borders. - const auto SHeight = 12+9; - - const auto Occ = 0x10000u; - const auto Empty = 0x12Eu, Border = Occ+0x7DBu, Garbage = Occ+0x6DBu; - - static unsigned long TimerRead() - { +/* A block puzzle game. + * Most of the source code comes from https://www.youtube.com/watch?v=V65mtR08fH0 + * Copyright (c) 2012 Joel Yliluoma (http://iki.fi/bisqwit/) + * License: MIT + */ + +// Standard C++ includes: +#include // for std::puts +#include // for std::rand +#include // for std::memcpy +#include // for std::abs, std::max, std::random_shuffle +#include // for std::pair +#include // for std::vector +#include +#include + +// Coroutine library. With thanks to Simon Tatham. +#define ccrVars struct ccrR { int p; ccrR() : p(0) { } } ccrL +#define ccrBegin(x) auto& ccrC=(x); switch(ccrC.ccrL.p) { case 0:; +#define ccrReturn(z) do { ccrC.ccrL.p=__LINE__; return(z); case __LINE__:; } while(0) +#define ccrFinish(z) } ccrC.ccrL.p=0; return(z) + +namespace ADLMIDI_PuzzleGame +{ + // Field & rendering definitions + const auto Width = 18, Height = 25; // Width and height, including borders. + const auto SHeight = 12+9; + + const auto Occ = 0x10000u; + const auto Empty = 0x12Eu, Border = Occ+0x7DBu, Garbage = Occ+0x6DBu; + + static unsigned long TimerRead() + { static std::chrono::time_point begin = std::chrono::system_clock::now(); - return 519 * std::chrono::duration( std::chrono::system_clock::now() - begin ).count(); - } - #define Timer TimerRead() - static void Sound(unsigned/*freq*/, unsigned/*duration*/) - { - } - static void PutCell(int x, int y, unsigned cell); - static void ScreenPutString(const char* str, unsigned attr, unsigned column, unsigned row) - { - for(; *str; ++column, ++str) - { - PutCell(column, row, ((unsigned char)(*str & 0xFF)) | (attr << 8)); - } - } - static char peeked_input = 0; - static bool kbhit() - { - if(peeked_input) return true; - peeked_input = Input.PeekInput(); - return peeked_input != 0; - } - static char getch() - { - char r = peeked_input; - peeked_input = 0; - return r; - } - - // Game engine - using uint64_t = std::uint_fast64_t; - struct Piece - { - struct { uint64_t s[4]; } shape; - int x:8, y:8, color:14; - unsigned r:2; - - template // walkthrough operator - inline bool operator>(T it) const - { - uint64_t p = 1ull, q = shape.s[r]; - for(int by=0; by<8; ++by) - for(int bx=0; bx<8; ++bx) - { - if((q & p) && it(x+bx, y+by)) return true; - //p >>= 1ull; - p <<= 1ull; - } - return false; - } - template // transmogrify operator - inline Piece operator*(T it) const { Piece res(*this); it(res); return res; } - }; - - template - struct TetrisArea - { - int Area[Height][Width]; - unsigned RenderX; - unsigned n_full, list_full, animx; - unsigned long timer; - struct { ccrVars; } cascadescope; - public: - TetrisArea(unsigned x=0) : RenderX(x) { } - - bool Occupied(int x,int y) const - { return x<1 || (x>Width-2) || (y>=0 && (Area[y][x] & Occ)); } - - template - inline void DrawRow(unsigned y, T get) - { - for(int x=1; x[&](int x,int y)->bool { this->DrawBlock(x,y,color); return false; }; - } - - bool CollidePiece(const Piece& piece) const - { - return piece>[&](int x,int y) { return this->Occupied(x,y); }; - } - bool CascadeEmpty(int FirstY) - { - if(DoDraw) - { - ccrBegin(cascadescope); - - // Count full lines - n_full = list_full = 0; - for(int y = std::max(0,FirstY); y < Height-1; ++y) - if(TestFully(y,true)) - { - ++n_full; - list_full |= 1u << y; - } - if(n_full) - { - // Clear all full lines in Tengen Tetris style. - for(animx = 1; animx < Width-1; ++animx) - { - for(timer=Timer; Timer= 0; --y) - if(!(list_full & (1u << y))) - DrawRow(target--, [&](unsigned x) { return this->Area[y][x]; }); - // Clear the top lines - for(auto n=n_full; n-- > 0; ) - DrawRow(target--, [](unsigned) { return Empty; }); - } - ccrFinish(false); - } - else - { - // Cascade non-empty lines - int target = Height-2, y = Height-2; - n_full = 0; - for(int miny = std::max(0,FirstY); y >= miny; --y) - if(TestFully(y, true)) - { - ++n_full; - miny = 0; - } - else - { - if(target != y) - memcpy(&Area[target], &Area[y], sizeof(Area[0][0])*Width); - --target; - } - // Clear the top lines - for(auto n=n_full; n-- > 0; ) - DrawRow(target--, [](unsigned) { return Empty; }); - return false; - } - } - }; - - template - class TetrisAIengine: TetrisArea - { - public://protected: - typedef std::pair scoring; - struct position - { - // 1. Rotate to this position - int rot; - // 2. Move to this column - int x; - // 3. Drop to ground - // 4. Rotate to this position - int rot2; - // 5. Move maximally to this direction - int x2; - // 6. Drop to ground again - }; - - std::vector positions1, positions2; - - struct Recursion - { - // Copy of the field before this recursion level - decltype(Area) bkup; - // Currently testing position - unsigned pos_no; - // Best outcome so far from this recursion level - scoring best_score, base_score; - position best_pos; - Recursion() : best_pos( {1,5, 1,0} ) { } - } data[8]; - unsigned ply, ply_limit; - - bool restart; // Begin a new set of calculations - bool confident; // false = Reset best-score - bool resting; // true = No calculations to do - bool doubting; - struct { ccrVars; } aiscope; - - public: - void AI_Signal(int signal) - { - // any = piece moved; 0 = new piece, 2 = at ground - switch(signal) - { - case 0: // new piece - // do full scan and reset score - confident = false; - restart = true; - resting = false; - doubting = false; - break; - case 1: // piece moved - // keep scanning (low prio), no resets - resting = false; - break; - case 2: // now at ground - // do full scan without resetting score - resting = false; - restart = true; - doubting = true; - break; - } - } - int AI_Run(const decltype(Area)& in_area, const Piece* seq) - { - std::memcpy(Area, in_area, sizeof(Area)); - - // For AI, we use Pierre Dellacherie's algorithm, - // but extended for arbitrary ply lookahead. - enum { - landingHeightPenalty = -1, erodedPieceCellMetricFactor = 2, - rowTransitionPenalty = -2, columnTransitionPenalty = -2, - buriedHolePenalty = -8, wellPenalty = -2/*, - occlusionPenalty = 0*/ }; - - ccrBegin(aiscope); - - positions1.clear(); - positions2.clear(); - for(int x=-1; x ( {int(rot),x,int(rot2),x2} ) ); - if(rot2 == rot && x2 == 0) - positions2.push_back( std::move ( {int(rot),x,int(rot2),x2} ) ); - } - - confident = false; - doubting = false; - Restart: - restart = false; - resting = false; - - /*std::random_shuffle(positions1.begin(), positions1.end());*/ - std::random_shuffle(positions2.begin(), positions2.end()); - - ply = 0; - - Recursion: - // Take current board as testing platform - std::memcpy(data[ply].bkup, Area, sizeof(Area)); - if(!confident || ply > 0) - { - data[ply].best_score = {-999999,0}; - if(ply == 0) - { - //int heap_room = 0; - //while(TestFully(heap_room,false)) ++heap_room; - ply_limit = 2; - } - } - for(;;) - { - data[ply].pos_no = 0; - do { - if(DoReturns) - { - ccrReturn(0); - if(restart) goto Restart; - } - if(ply > 0) - { - // Game might have changed the the board contents. - std::memcpy(data[0].bkup, in_area, sizeof(Area)); - - // Now go on and test with the current testing platform - std::memcpy(Area, data[ply].bkup, sizeof(Area)); - } - - // Fix the piece in place, cascade rows, and analyze the result. - { const position& goal = (/*ply==0&&!doubting ? positions1 :*/ positions2) - [ data[ply].pos_no ]; - Piece n = seq[ply]; - n.x = goal.x; - n.r = goal.rot; - if(ply) n.y = 0; - - // If we are analyzing a mid-fall maneuver, verify whether - // the piece can be actually maneuvered into this position. - //if(ply==0 && n.y >= 0) - for(Piece q,t=*seq; t.x!=n.x && t.r!=n.r; ) - if( (t.r == n.r || (q=t, ++t.r, CollidePiece(t)&&(t=q,true))) - && (t.x <= n.x || (q=t, --t.x, CollidePiece(t)&&(t=q,true))) - && (t.x >= n.x || (q=t, ++t.x, CollidePiece(t)&&(t=q,true)))) - goto next_move; // no method of maneuvering. - - // Land the piece if it's not already landed - do ++n.y; while(!CollidePiece(n)); --n.y; - if(n.y < 0 || CollidePiece(n)) goto next_move; // cannot place piece? - - /* - // Rotate to ground-rotation - if(n.r != goal.rot2 || goal.x2 != 0) - { - while(n.r != goal.rot2) - { - ++n.r; - if(CollidePiece(n)) goto next_move; - } - if(goal.x2 != 0) - { - do n.x += goal.x2; while(!CollidePiece(n)); - n.x -= goal.x2; - } - - do ++n.y; while(!CollidePiece(n)); --n.y; - if(n.y < 0 || CollidePiece(n)) goto next_move; // cannot place piece? - } - */ - - DrawPiece(n, Occ); // place piece - - // Find out the extents of this piece, and how many - // cells of the piece contribute into full (completed) rows. - char full[4]={-1,-1,-1,-1}; - int miny=n.y+9, maxy=n.y-9, minx=n.x+9, maxx=n.x-9, num_eroded=0; - n>[&](int x,int y) -> bool - { if(x < minx) {minx = x;} if(x > maxx) {maxx = x;} - if(y < miny) {miny = y;} if(y > maxy) {maxy = y;} - if(full[y - n.y] < 0) full[y - n.y] = this->TestFully(y,true); - num_eroded += full[y - n.y]; - return false; }; - - CascadeEmpty(n.y); - - // Analyze the board and assign penalties - int penalties = 0; - for(int y=0; y 0) - { - int v = data[ply].best_score.first; - v /= 2; - //if(ply_limit == 4) v /= 2; else v *= 2; - data[ply-1].base_score.first += v;// >> (2-ply); - goto Unrecursion; - /* - parent += child / 2 : - Game 0x7ffff0d94ce0 over with score=91384,lines=92 - Game 0x7ffff0d96a40 over with score=153256,lines=114 - parent += child : - Game 0x7fff4a4eb8a0 over with score=83250,lines=86 - Game 0x7fff4a4ed600 over with score=295362,lines=166 - parent += child * 2 : - Game 0x7fff000a2e00 over with score=182306,lines=131 - Game 0x7fff000a10a0 over with score=383968,lines=193 - parent += child * 4 : - Game 0x7fff267867b0 over with score=62536,lines=75 - Game 0x7fff26788510 over with score=156352,lines=114 - */ - } - // all scanned; unless piece placement changes we're as good as we can be. - confident = true; - resting = true; - doubting = false; - while(resting) ccrReturn(0); - if(restart) goto Restart; - } // infinite refining loop - ccrFinish(0); - } - }; - - class Tetris: protected TetrisArea - { - public: - Tetris(unsigned rx) : TetrisArea(rx), seq(),hiscore(0),hudtimer(0) {} - virtual ~Tetris() { } - - protected: - // These variables should be local to GameLoop(), - // but because of coroutines, they must be stored - // in a persistent wrapper instead. Such persistent - // wrapper is provided by the game object itself. - Piece seq[4]; - unsigned hiscore, score, lines, combo, pieces; - unsigned long hudtimer; - bool escaped, dropping, ticked, kicked, spinned, atground, first; - - struct { ccrVars; } loopscope; - public: - unsigned incoming; - - int GameLoop() - { - Piece &cur = *seq; - ccrBegin(loopscope); - - // Initialize area - for(auto y=Height; y-- > 0; ) - for(auto x=Width; x-- > 0; ) - DrawBlock(x,y, (x>0&&x> (rand()%10); - DrawRow(y, [&](unsigned x) { return - y < threshold - ? Area[y+incoming][x] - : (mask & (1< 0 && CollidePiece(cur); --cur.y) {} - incoming = 0; - goto re_collide; - } - - ticked = Timer >= timer + std::max(atground?40:10, int( ((17-Level())*8)) ); - if(ticked || MyKbHit() || dropping) break; - } - - Piece n = cur; - if(MyKbHit()) dropping = false; - - switch(ticked ? 's' : (MyKbHit() ? MyGetCh() : (dropping ? 's' : 0))) - { - case 'H'<<8: case 'w': ++n.r; /*Sound(120, 1);*/ break; - case 'P'<<8: case 's': ++n.y; break; - case 'K'<<8: case 'a': --n.x; /*Sound(120, 1);*/ break; - case 'M'<<8: case 'd': ++n.x; /*Sound(120, 1);*/ break; - case 'q': case '\033': /*escaped = true;*/ break; - case ' ': dropping = true; /*fallthrough*/ - default: continue; - } - if(n.x != cur.x) kicked = false; - - if(CollidePiece(n)) - { - if(n.y == cur.y+1) break; // fix piece if collide against ground - // If tried rotating, and was unsuccessful, try wall kicks - if(n.r != cur.r && !CollidePiece(n*[](Piece&p){++p.x;})) { kicked = true; ++n.x; } else - if(n.r != cur.r && !CollidePiece(n*[](Piece&p){--p.x;})) { kicked = true; --n.x; } else - continue; // no move - } - DrawPiece(cur, Empty); - if(n.y > cur.y) timer = Timer; // Reset autodrop timer - cur = n; - } - - // If the piece cannot be moved sideways or up from its final position, - // determine that it must have been spin-fixed into its place. - // It is a bonus-worthy accomplishment if it ends up clearing lines. - spinned = CollidePiece(cur*[](Piece&p){--p.y;}) - && CollidePiece(cur*[](Piece&p){--p.x;}) - && CollidePiece(cur*[](Piece&p){++p.x;}); - DrawPiece(cur, cur.color|Occ); - Sound(50, 30); - - while(CascadeEmpty(cur.y)) ccrReturn(0); - if(n_full > 1) ccrReturn(n_full-1); // Send these rows to opponent - - pieces += 1; - lines += n_full; - static const unsigned clr[] = {0,1,3,5,8, 13,21,34,45}; - int multiplier = Level(), clears = clr[n_full]; - int bonus = (clears * 100 + (cur.y*50/Height)); - int extra = 0; - if(spinned) extra = ((clears+1) * ((kicked ? 3 : 4)/2) - clears) * 100; - combo = n_full ? combo + n_full : 0; - int comboscore = combo > n_full ? combo*50*multiplier : 0; - bonus *= multiplier; - extra *= multiplier; - score += bonus + extra + comboscore; - HUD_Add(bonus, extra, comboscore); - //if(n_full) std::fprintf(stdout, "Game %p += %u lines -> score=%u,lines=%u\n", this, n_full, score,lines); - } - //std::fprintf(stdout, "Game %p over with score=%u,lines=%u\n", this, score,lines); - //over_lines += lines; - - if(score > hiscore) hiscore = score; - HudPrint(7, 4, "", "%-7u", hiscore); - - ccrFinish(-1); - } - - protected: - int Level() const { return 1 + lines/10; } - - void MakeNext() - { - const int which = 2; // Index within seq[] to populate - static const Piece b[] = - { - { { { 0x04040404,0x00000F00,0x04040404,0x00000F00 } }, 0,0, 0xBDB,0 }, // I - { { { 0x0000080E,0x000C0808,0x00000E02,0x00020206 } }, 0,0, 0x3DB,0 }, // J - { { { 0x0000020E,0x0008080C,0x00000E08,0x00060202 } }, 0,0, 0x6DB,0 }, // L - { { { 0x00000606,0x00000606,0x00000606,0x00000606 } }, 0,0, 0xEDB,0 }, // O - { { { 0x00080C04,0x0000060C,0x00080C04,0x0000060C } }, 0,0, 0xADB,0 }, // S - { { { 0x00000E04,0x00040C04,0x00040E00,0x00040604 } }, 0,0, 0x5DB,0 }, // T - { { { 0x00020604,0x00000C06,0x00020604,0x00000C06 } }, 0,0, 0x4DB,0 }, // Z - // Add some pentaminos to create a challenge worth the AI: - { { { 0x00020702,0x00020702,0x00020702,0x00020702 } }, 0,0, 0x2DB,0 }, // + - { { { 0x000E0404,0x00020E02,0x0004040E,0x00080E08 } }, 0,0, 0x9DB,0 }, // T5 - { { { 0x00000A0E,0x000C080C,0x00000E0A,0x00060206 } }, 0,0, 0x3DB,0 }, // C - { { { 0x00060604,0x000E0600,0x02060600,0x00060700 } }, 0,0, 0x7DB,0 }, // P - { { { 0x00060602,0x00070600,0x04060600,0x00060E00 } }, 0,0, 0xFDB,0 }, // Q - { { { 0x04040404,0x00000F00,0x04040404,0x00000F00 } }, 0,0, 0xBDB,0 }, // I - { { { 0x00040702,0x00030602,0x00020701,0x00020306 } }, 0,0, 0xDDB,0 }, // R - { { { 0x00010702,0x00020603,0x00020704,0x00060302 } }, 0,0, 0x8DB,0 }, // F - // I is included twice, otherwise it's just a bit too unlikely. - }; - int c = Empty; - auto fx = [this]() { seq[0].x=1; seq[0].y=-1; - seq[1].x=Width+1; seq[1].y=SHeight-8; - seq[2].x=Width+5; seq[2].y=SHeight-8; - /*seq[3].x=Width+9; seq[3].y=SHeight-8; - seq[4].x=Width+1; seq[4].y=SHeight-4; - seq[5].x=Width+5; seq[5].y=SHeight-4; - seq[6].x=Width+9; seq[6].y=SHeight-4;*/ - }; - auto db = [&](int x,int y) - { - PutCell(x+RenderX, y, c); - return false; - }; - fx(); - seq[1]>db; seq[0] = seq[1]; - seq[2]>db; seq[1] = seq[2]; - /*seq[3]>db; seq[2] = seq[3]; - seq[4]>db; seq[3] = seq[4]; - seq[5]>db; seq[4] = seq[5]; - seq[6]>db; seq[5] = seq[6];*/ - fx(); - const unsigned npieces = sizeof(b) / sizeof(*b); - unsigned rnd = (std::rand()/double(RAND_MAX)) * (4*npieces); - #ifndef EASY - /*if(pieces > 3) - { - int heap_room = 0; - while(TestFully(heap_room,false)) ++heap_room; - bool cheat_good = heap_room <= (7 + lines/20); - bool cheat_evil = heap_room >= 18; - if(heap_room >= 16 && (pieces % 5) == 0) cheat_good = false; - if( (pieces % 11) == 0) cheat_good = true; - if(cheat_good) cheat_evil = false; - if(cheat_good || cheat_evil) - { - // Don't tell anyone, but in desperate situations, - // we let AI judge what's best for upcoming pieces, - // in order to prolong the game! - // So, the AI cheats, but it is an equal benefactor. - // EXCEPTION: if there is an abundance of space, - // bring out the _worst_ possible pieces! - TetrisAIengine<>::scoring best_score; - unsigned best_choice = ~0u; - for(unsigned test=0; test chooser; - chooser.AI_Signal(0); - chooser.AI_Run(Area, seq); - auto& s = chooser.data[0].best_score; - if(best_choice == ~0u - || (cheat_evil ? s < best_score : s > best_score) - ) - { best_score = s; best_choice = choice; } - } - rnd = best_choice * 4 + (rnd % 4); - } - }*/ - #endif - seq[which] = b[rnd / 4]; - seq[which].r = rnd % 4; - fx(); - c=seq[1].color; seq[1]>db; - c=(seq[2].color&0xF00) + 176; seq[2]>db; - /*c=(seq[3].color&0xF00) + 176; seq[3]>db; - c=(seq[4].color&0xF00) + 176; seq[4]>db; - c=(seq[5].color&0xF00) + 176; seq[5]>db; - c=(seq[6].color&0xF00) + 176; seq[6]>db;*/ - HudPrint(0, SHeight-9, "NEXT:","",0); - HudPrint(12, 0, "SCORE:", "%-7u", score); - HudPrint(12, 2, "LINES:", "%-5u", lines); - HudPrint(12, 4, "LEVEL:", "%-3u", Level()); - } - - void HudPrint(int c, int y,const char*a,const char*b,int v=0) const - { - char Buf[64]; - snprintf(Buf, sizeof Buf, b, v); - ScreenPutString(a, 15, RenderX+Width+2, y); - ScreenPutString(Buf, c, RenderX+Width+2, y+1); - } - - void HUD_Run() - { - if(!hudtimer || Timer < hudtimer) return; - HUD_Add(0,0,0); - hudtimer = 0; - } - void HUD_Add(int bonus, int extra, int combo) - { - hudtimer = Timer + 180; - static const char blank[] = " "; - HudPrint(10, 6,bonus?blank :blank,bonus?"%+-6d":blank, bonus); - HudPrint(10, 8,combo?"Combo":blank,combo?"%+-6d":blank, combo); - HudPrint(13, 10,extra?"Skill":blank,extra?"%+-6d":blank, extra); - } - - virtual void AI_Signal(int) { } - virtual int AI_Run() { return 0; } - virtual int MyKbHit() = 0; - virtual int MyGetCh() = 0; - public: - // Return -1 = don't want sleep, +1 = want sleep, 0 = don't mind - virtual char DelayOpinion() const { return 0; }//dropping ? -1 : 0; } - }; - - class TetrisHuman: public Tetris - { - public: - TetrisHuman(unsigned rx) : Tetris(rx) { } - protected: - virtual int MyKbHit() { return kbhit(); } - virtual int MyGetCh() { int c; return (c = getch()) ? c : (getch() << 8); } - }; - - class TetrisAI: public Tetris, TetrisAIengine - { - protected: - virtual void AI_Signal(int s) { TetrisAIengine::AI_Signal(s); } - virtual int AI_Run() - { - #ifdef __DJGPP__ - char buf1[16]/*, buf2[16], buf3[16]*/; - sprintf(buf1, "COM%u%c%c%c%c", ply_limit, - resting ? 'r' : '_', - confident ? 'C' : '_', - doubting ? 'D' : '_', - atground ? 'G' : '_' - ); - ScreenPutString(buf1, 0x70, Tetris::RenderX+Width, 0); - /* - sprintf(buf2, "%-8d", data[0].best_score.first); - sprintf(buf3, "%2d,%d", data[0].best_pos.x, - data[0].best_pos.rot); - ScreenPutString(buf2, 0x08, Tetris::RenderX+Width, 1); - ScreenPutString(buf3, 0x08, Tetris::RenderX+Width, 4); - */ - #endif - return TetrisAIengine::AI_Run(Tetris::Area, seq); - } - virtual int MyKbHit() - { - return PendingAIinput ? 1 : (PendingAIinput = GenerateAIinput()) != 0; - } - virtual int MyGetCh() - { - int r = PendingAIinput; - return r ? (PendingAIinput=0), r : GenerateAIinput(); - } - char d(char c, int maxdelay) - { - if(Timer >= intimer+maxdelay) { intimer=Timer; return c; } - return 0; - } - int GenerateAIinput() - { - /*if(TetrisAIengine::atground) - { - if(seq->r != data[0].best_pos.rot2) return d('w',1); - if(data[0].best_pos.x2 < 0) return d('a', 1); - if(data[0].best_pos.x2 > 0) return d('d', 1); - } - else*/ - { - if(seq->r != data[0].best_pos.rot) return d('w',1); - if(seq->y >= 0 && seq->x > data[0].best_pos.x) return d('a', 2); - if(seq->y >= 0 && seq->x < data[0].best_pos.x) return d('d', 2); - } - if(doubting || !confident) return 0; - return d('s',3); - } - - int PendingAIinput, delay; - unsigned long intimer; - public: - virtual char DelayOpinion() const - { if(doubting || restart || !confident) return -1; - return Tetris::DelayOpinion(); } - - TetrisAI(unsigned rx) : Tetris(rx), PendingAIinput(0), delay(0), intimer(0) {} - }; - #undef Timer -} + return (unsigned long)(519 * std::chrono::duration( std::chrono::system_clock::now() - begin ).count()); + } + #define Timer TimerRead() + static void Sound(unsigned/*freq*/, unsigned/*duration*/) + { + } + static void PutCell(int x, int y, unsigned cell); + static void ScreenPutString(const char* str, unsigned attr, unsigned column, unsigned row) + { + for(; *str; ++column, ++str) + { + PutCell(column, row, ((unsigned char)(*str & 0xFF)) | (attr << 8)); + } + } + static char peeked_input = 0; + static bool kbhit() + { + if(peeked_input) return true; + peeked_input = Input.PeekInput(); + return peeked_input != 0; + } + static char getch() + { + char r = peeked_input; + peeked_input = 0; + return r; + } + + // Game engine + using uint64_t = std::uint_fast64_t; + struct Piece + { + struct { uint64_t s[4]; } shape; + int x:8, y:8, color:14; + unsigned r:2; + + template // walkthrough operator + inline bool operator>(T it) const + { + uint64_t p = 1ull, q = shape.s[r]; + for(int by=0; by<8; ++by) + for(int bx=0; bx<8; ++bx) + { + if((q & p) && it(x+bx, y+by)) return true; + //p >>= 1ull; + p <<= 1ull; + } + return false; + } + template // transmogrify operator + inline Piece operator*(T it) const { Piece res(*this); it(res); return res; } + }; + + template + struct TetrisArea + { + int Area[Height][Width]; + unsigned RenderX; + unsigned n_full, list_full, animx; + unsigned long timer; + struct { ccrVars; } cascadescope; + public: + TetrisArea(unsigned x=0) : RenderX(x) { } + + bool Occupied(int x,int y) const + { return x<1 || (x>Width-2) || (y>=0 && (Area[y][x] & Occ)); } + + template + inline void DrawRow(unsigned y, T get) + { + for(int x=1; x[&](int x,int y)->bool { this->DrawBlock(x,y,color); return false; }; + } + + bool CollidePiece(const Piece& piece) const + { + return piece>[&](int x,int y) { return this->Occupied(x,y); }; + } + bool CascadeEmpty(int FirstY) + { + if(DoDraw) + { + ccrBegin(cascadescope); + + // Count full lines + n_full = list_full = 0; + for(int y = std::max(0,FirstY); y < Height-1; ++y) + if(TestFully(y,true)) + { + ++n_full; + list_full |= 1u << y; + } + if(n_full) + { + // Clear all full lines in Tengen Tetris style. + for(animx = 1; animx < Width-1; ++animx) + { + for(timer=Timer; Timer= 0; --y) + if(!(list_full & (1u << y))) + DrawRow(target--, [&](unsigned x) { return this->Area[y][x]; }); + // Clear the top lines + for(auto n=n_full; n-- > 0; ) + DrawRow(target--, [](unsigned) { return Empty; }); + } + ccrFinish(false); + } + else + { + // Cascade non-empty lines + int target = Height-2, y = Height-2; + n_full = 0; + for(int miny = std::max(0,FirstY); y >= miny; --y) + if(TestFully(y, true)) + { + ++n_full; + miny = 0; + } + else + { + if(target != y) + memcpy(&Area[target], &Area[y], sizeof(Area[0][0])*Width); + --target; + } + // Clear the top lines + for(auto n=n_full; n-- > 0; ) + DrawRow(target--, [](unsigned) { return Empty; }); + return false; + } + } + }; + + template + class TetrisAIengine: TetrisArea + { + public://protected: + typedef std::pair scoring; + struct position + { + // 1. Rotate to this position + int rot; + // 2. Move to this column + int x; + // 3. Drop to ground + // 4. Rotate to this position + int rot2; + // 5. Move maximally to this direction + int x2; + // 6. Drop to ground again + }; + + std::vector positions1, positions2; + + struct Recursion + { + // Copy of the field before this recursion level + decltype(Area) bkup; + // Currently testing position + unsigned pos_no; + // Best outcome so far from this recursion level + scoring best_score, base_score; + position best_pos; + Recursion() : best_pos( {1,5, 1,0} ) { } + } data[8]; + unsigned ply, ply_limit; + + bool restart; // Begin a new set of calculations + bool confident; // false = Reset best-score + bool resting; // true = No calculations to do + bool doubting; + struct { ccrVars; } aiscope; + + public: + void AI_Signal(int signal) + { + // any = piece moved; 0 = new piece, 2 = at ground + switch(signal) + { + case 0: // new piece + // do full scan and reset score + confident = false; + restart = true; + resting = false; + doubting = false; + break; + case 1: // piece moved + // keep scanning (low prio), no resets + resting = false; + break; + case 2: // now at ground + // do full scan without resetting score + resting = false; + restart = true; + doubting = true; + break; + } + } + int AI_Run(const decltype(Area)& in_area, const Piece* seq) + { + std::memcpy(Area, in_area, sizeof(Area)); + + // For AI, we use Pierre Dellacherie's algorithm, + // but extended for arbitrary ply lookahead. + enum { + landingHeightPenalty = -1, erodedPieceCellMetricFactor = 2, + rowTransitionPenalty = -2, columnTransitionPenalty = -2, + buriedHolePenalty = -8, wellPenalty = -2/*, + occlusionPenalty = 0*/ }; + + ccrBegin(aiscope); + + positions1.clear(); + positions2.clear(); + for(int x=-1; x ( {int(rot),x,int(rot2),x2} ) ); + if(rot2 == rot && x2 == 0) + positions2.push_back( std::move ( {int(rot),x,int(rot2),x2} ) ); + } + + confident = false; + doubting = false; + Restart: + restart = false; + resting = false; + + /*std::random_shuffle(positions1.begin(), positions1.end());*/ + std::random_shuffle(positions2.begin(), positions2.end()); + + ply = 0; + + Recursion: + // Take current board as testing platform + std::memcpy(data[ply].bkup, Area, sizeof(Area)); + if(!confident || ply > 0) + { + data[ply].best_score = {-999999,0}; + if(ply == 0) + { + //int heap_room = 0; + //while(TestFully(heap_room,false)) ++heap_room; + ply_limit = 2; + } + } + for(;;) + { + data[ply].pos_no = 0; + do { + if(DoReturns) + { + ccrReturn(0); + if(restart) goto Restart; + } + if(ply > 0) + { + // Game might have changed the the board contents. + std::memcpy(data[0].bkup, in_area, sizeof(Area)); + + // Now go on and test with the current testing platform + std::memcpy(Area, data[ply].bkup, sizeof(Area)); + } + + // Fix the piece in place, cascade rows, and analyze the result. + { const position& goal = (/*ply==0&&!doubting ? positions1 :*/ positions2) + [ data[ply].pos_no ]; + Piece n = seq[ply]; + n.x = goal.x; + n.r = goal.rot; + if(ply) n.y = 0; + + // If we are analyzing a mid-fall maneuver, verify whether + // the piece can be actually maneuvered into this position. + //if(ply==0 && n.y >= 0) + for(Piece q,t=*seq; t.x!=n.x && t.r!=n.r; ) + if( (t.r == n.r || (q=t, ++t.r, CollidePiece(t)&&(t=q,true))) + && (t.x <= n.x || (q=t, --t.x, CollidePiece(t)&&(t=q,true))) + && (t.x >= n.x || (q=t, ++t.x, CollidePiece(t)&&(t=q,true)))) + goto next_move; // no method of maneuvering. + + // Land the piece if it's not already landed + do ++n.y; while(!CollidePiece(n)); --n.y; + if(n.y < 0 || CollidePiece(n)) goto next_move; // cannot place piece? + + /* + // Rotate to ground-rotation + if(n.r != goal.rot2 || goal.x2 != 0) + { + while(n.r != goal.rot2) + { + ++n.r; + if(CollidePiece(n)) goto next_move; + } + if(goal.x2 != 0) + { + do n.x += goal.x2; while(!CollidePiece(n)); + n.x -= goal.x2; + } + + do ++n.y; while(!CollidePiece(n)); --n.y; + if(n.y < 0 || CollidePiece(n)) goto next_move; // cannot place piece? + } + */ + + DrawPiece(n, Occ); // place piece + + // Find out the extents of this piece, and how many + // cells of the piece contribute into full (completed) rows. + char full[4]={-1,-1,-1,-1}; + int miny=n.y+9, maxy=n.y-9, minx=n.x+9, maxx=n.x-9, num_eroded=0; + n>[&](int x,int y) -> bool + { if(x < minx) {minx = x;} if(x > maxx) {maxx = x;} + if(y < miny) {miny = y;} if(y > maxy) {maxy = y;} + if(full[y - n.y] < 0) full[y - n.y] = this->TestFully(y,true); + num_eroded += full[y - n.y]; + return false; }; + + CascadeEmpty(n.y); + + // Analyze the board and assign penalties + int penalties = 0; + for(int y=0; y 0) + { + int v = data[ply].best_score.first; + v /= 2; + //if(ply_limit == 4) v /= 2; else v *= 2; + data[ply-1].base_score.first += v;// >> (2-ply); + goto Unrecursion; + /* + parent += child / 2 : + Game 0x7ffff0d94ce0 over with score=91384,lines=92 + Game 0x7ffff0d96a40 over with score=153256,lines=114 + parent += child : + Game 0x7fff4a4eb8a0 over with score=83250,lines=86 + Game 0x7fff4a4ed600 over with score=295362,lines=166 + parent += child * 2 : + Game 0x7fff000a2e00 over with score=182306,lines=131 + Game 0x7fff000a10a0 over with score=383968,lines=193 + parent += child * 4 : + Game 0x7fff267867b0 over with score=62536,lines=75 + Game 0x7fff26788510 over with score=156352,lines=114 + */ + } + // all scanned; unless piece placement changes we're as good as we can be. + confident = true; + resting = true; + doubting = false; + while(resting) ccrReturn(0); + if(restart) goto Restart; + } // infinite refining loop + ccrFinish(0); + } + }; + + class Tetris: protected TetrisArea + { + public: + Tetris(unsigned rx) : TetrisArea(rx), seq(),hiscore(0),hudtimer(0) {} + virtual ~Tetris() { } + + protected: + // These variables should be local to GameLoop(), + // but because of coroutines, they must be stored + // in a persistent wrapper instead. Such persistent + // wrapper is provided by the game object itself. + Piece seq[4]; + unsigned hiscore, score, lines, combo, pieces; + unsigned long hudtimer; + bool escaped, dropping, ticked, kicked, spinned, atground, first; + + struct { ccrVars; } loopscope; + public: + unsigned incoming; + + int GameLoop() + { + Piece &cur = *seq; + ccrBegin(loopscope); + + // Initialize area + for(auto y=Height; y-- > 0; ) + for(auto x=Width; x-- > 0; ) + DrawBlock(x,y, (x>0&&x> (rand()%10); + DrawRow(y, [&](unsigned x) { return + y < threshold + ? Area[y+incoming][x] + : (mask & (1< 0 && CollidePiece(cur); --cur.y) {} + incoming = 0; + goto re_collide; + } + + ticked = Timer >= timer + std::max(atground?40:10, int( ((17-Level())*8)) ); + if(ticked || MyKbHit() || dropping) break; + } + + Piece n = cur; + if(MyKbHit()) dropping = false; + + switch(ticked ? 's' : (MyKbHit() ? MyGetCh() : (dropping ? 's' : 0))) + { + case 'H'<<8: case 'w': ++n.r; /*Sound(120, 1);*/ break; + case 'P'<<8: case 's': ++n.y; break; + case 'K'<<8: case 'a': --n.x; /*Sound(120, 1);*/ break; + case 'M'<<8: case 'd': ++n.x; /*Sound(120, 1);*/ break; + case 'q': case '\033': /*escaped = true;*/ break; + case ' ': dropping = true; /*fallthrough*/ + default: continue; + } + if(n.x != cur.x) kicked = false; + + if(CollidePiece(n)) + { + if(n.y == cur.y+1) break; // fix piece if collide against ground + // If tried rotating, and was unsuccessful, try wall kicks + if(n.r != cur.r && !CollidePiece(n*[](Piece&p){++p.x;})) { kicked = true; ++n.x; } else + if(n.r != cur.r && !CollidePiece(n*[](Piece&p){--p.x;})) { kicked = true; --n.x; } else + continue; // no move + } + DrawPiece(cur, Empty); + if(n.y > cur.y) timer = Timer; // Reset autodrop timer + cur = n; + } + + // If the piece cannot be moved sideways or up from its final position, + // determine that it must have been spin-fixed into its place. + // It is a bonus-worthy accomplishment if it ends up clearing lines. + spinned = CollidePiece(cur*[](Piece&p){--p.y;}) + && CollidePiece(cur*[](Piece&p){--p.x;}) + && CollidePiece(cur*[](Piece&p){++p.x;}); + DrawPiece(cur, cur.color|Occ); + Sound(50, 30); + + while(CascadeEmpty(cur.y)) ccrReturn(0); + if(n_full > 1) ccrReturn(n_full-1); // Send these rows to opponent + + pieces += 1; + lines += n_full; + static const unsigned clr[] = {0,1,3,5,8, 13,21,34,45}; + int multiplier = Level(), clears = clr[n_full]; + int bonus = (clears * 100 + (cur.y*50/Height)); + int extra = 0; + if(spinned) extra = ((clears+1) * ((kicked ? 3 : 4)/2) - clears) * 100; + combo = n_full ? combo + n_full : 0; + int comboscore = combo > n_full ? combo*50*multiplier : 0; + bonus *= multiplier; + extra *= multiplier; + score += bonus + extra + comboscore; + HUD_Add(bonus, extra, comboscore); + //if(n_full) std::fprintf(stdout, "Game %p += %u lines -> score=%u,lines=%u\n", this, n_full, score,lines); + } + //std::fprintf(stdout, "Game %p over with score=%u,lines=%u\n", this, score,lines); + //over_lines += lines; + + if(score > hiscore) hiscore = score; + HudPrint(7, 4, "", "%-7u", hiscore); + + ccrFinish(-1); + } + + protected: + int Level() const { return 1 + lines/10; } + + void MakeNext() + { + const int which = 2; // Index within seq[] to populate + static const Piece b[] = + { + { { { 0x04040404,0x00000F00,0x04040404,0x00000F00 } }, 0,0, 0xBDB,0 }, // I + { { { 0x0000080E,0x000C0808,0x00000E02,0x00020206 } }, 0,0, 0x3DB,0 }, // J + { { { 0x0000020E,0x0008080C,0x00000E08,0x00060202 } }, 0,0, 0x6DB,0 }, // L + { { { 0x00000606,0x00000606,0x00000606,0x00000606 } }, 0,0, 0xEDB,0 }, // O + { { { 0x00080C04,0x0000060C,0x00080C04,0x0000060C } }, 0,0, 0xADB,0 }, // S + { { { 0x00000E04,0x00040C04,0x00040E00,0x00040604 } }, 0,0, 0x5DB,0 }, // T + { { { 0x00020604,0x00000C06,0x00020604,0x00000C06 } }, 0,0, 0x4DB,0 }, // Z + // Add some pentaminos to create a challenge worth the AI: + { { { 0x00020702,0x00020702,0x00020702,0x00020702 } }, 0,0, 0x2DB,0 }, // + + { { { 0x000E0404,0x00020E02,0x0004040E,0x00080E08 } }, 0,0, 0x9DB,0 }, // T5 + { { { 0x00000A0E,0x000C080C,0x00000E0A,0x00060206 } }, 0,0, 0x3DB,0 }, // C + { { { 0x00060604,0x000E0600,0x02060600,0x00060700 } }, 0,0, 0x7DB,0 }, // P + { { { 0x00060602,0x00070600,0x04060600,0x00060E00 } }, 0,0, 0xFDB,0 }, // Q + { { { 0x04040404,0x00000F00,0x04040404,0x00000F00 } }, 0,0, 0xBDB,0 }, // I + { { { 0x00040702,0x00030602,0x00020701,0x00020306 } }, 0,0, 0xDDB,0 }, // R + { { { 0x00010702,0x00020603,0x00020704,0x00060302 } }, 0,0, 0x8DB,0 }, // F + // I is included twice, otherwise it's just a bit too unlikely. + }; + int c = Empty; + auto fx = [this]() { seq[0].x=1; seq[0].y=-1; + seq[1].x=Width+1; seq[1].y=SHeight-8; + seq[2].x=Width+5; seq[2].y=SHeight-8; + /*seq[3].x=Width+9; seq[3].y=SHeight-8; + seq[4].x=Width+1; seq[4].y=SHeight-4; + seq[5].x=Width+5; seq[5].y=SHeight-4; + seq[6].x=Width+9; seq[6].y=SHeight-4;*/ + }; + auto db = [&](int x,int y) + { + PutCell(x+RenderX, y, c); + return false; + }; + fx(); + seq[1]>db; seq[0] = seq[1]; + seq[2]>db; seq[1] = seq[2]; + /*seq[3]>db; seq[2] = seq[3]; + seq[4]>db; seq[3] = seq[4]; + seq[5]>db; seq[4] = seq[5]; + seq[6]>db; seq[5] = seq[6];*/ + fx(); + const unsigned npieces = sizeof(b) / sizeof(*b); + unsigned rnd = (unsigned)((std::rand()/double(RAND_MAX)) * (4 * npieces)); + #ifndef EASY + /*if(pieces > 3) + { + int heap_room = 0; + while(TestFully(heap_room,false)) ++heap_room; + bool cheat_good = heap_room <= (7 + lines/20); + bool cheat_evil = heap_room >= 18; + if(heap_room >= 16 && (pieces % 5) == 0) cheat_good = false; + if( (pieces % 11) == 0) cheat_good = true; + if(cheat_good) cheat_evil = false; + if(cheat_good || cheat_evil) + { + // Don't tell anyone, but in desperate situations, + // we let AI judge what's best for upcoming pieces, + // in order to prolong the game! + // So, the AI cheats, but it is an equal benefactor. + // EXCEPTION: if there is an abundance of space, + // bring out the _worst_ possible pieces! + TetrisAIengine<>::scoring best_score; + unsigned best_choice = ~0u; + for(unsigned test=0; test chooser; + chooser.AI_Signal(0); + chooser.AI_Run(Area, seq); + auto& s = chooser.data[0].best_score; + if(best_choice == ~0u + || (cheat_evil ? s < best_score : s > best_score) + ) + { best_score = s; best_choice = choice; } + } + rnd = best_choice * 4 + (rnd % 4); + } + }*/ + #endif + seq[which] = b[rnd / 4]; + seq[which].r = rnd % 4; + fx(); + c=seq[1].color; seq[1]>db; + c=(seq[2].color&0xF00) + 176; seq[2]>db; + /*c=(seq[3].color&0xF00) + 176; seq[3]>db; + c=(seq[4].color&0xF00) + 176; seq[4]>db; + c=(seq[5].color&0xF00) + 176; seq[5]>db; + c=(seq[6].color&0xF00) + 176; seq[6]>db;*/ + HudPrint(0, SHeight-9, "NEXT:","",0); + HudPrint(12, 0, "SCORE:", "%-7u", score); + HudPrint(12, 2, "LINES:", "%-5u", lines); + HudPrint(12, 4, "LEVEL:", "%-3u", Level()); + } + + void HudPrint(int c, int y,const char*a,const char*b,int v=0) const + { + char Buf[64]; + snprintf(Buf, sizeof Buf, b, v); + ScreenPutString(a, 15, RenderX+Width+2, y); + ScreenPutString(Buf, c, RenderX+Width+2, y+1); + } + + void HUD_Run() + { + if(!hudtimer || Timer < hudtimer) return; + HUD_Add(0,0,0); + hudtimer = 0; + } + void HUD_Add(int bonus, int extra, int combo) + { + hudtimer = Timer + 180; + static const char blank[] = " "; + HudPrint(10, 6,bonus?blank :blank,bonus?"%+-6d":blank, bonus); + HudPrint(10, 8,combo?"Combo":blank,combo?"%+-6d":blank, combo); + HudPrint(13, 10,extra?"Skill":blank,extra?"%+-6d":blank, extra); + } + + virtual void AI_Signal(int) { } + virtual int AI_Run() { return 0; } + virtual int MyKbHit() = 0; + virtual int MyGetCh() = 0; + public: + // Return -1 = don't want sleep, +1 = want sleep, 0 = don't mind + virtual char DelayOpinion() const { return 0; }//dropping ? -1 : 0; } + }; + + class TetrisHuman: public Tetris + { + public: + TetrisHuman(unsigned rx) : Tetris(rx) { } + protected: + virtual int MyKbHit() { return kbhit(); } + virtual int MyGetCh() { int c; return (c = getch()) ? c : (getch() << 8); } + }; + + class TetrisAI: public Tetris, TetrisAIengine + { + protected: + virtual void AI_Signal(int s) { TetrisAIengine::AI_Signal(s); } + virtual int AI_Run() + { + #ifdef __DJGPP__ + char buf1[16]/*, buf2[16], buf3[16]*/; + sprintf(buf1, "COM%u%c%c%c%c", ply_limit, + resting ? 'r' : '_', + confident ? 'C' : '_', + doubting ? 'D' : '_', + atground ? 'G' : '_' + ); + ScreenPutString(buf1, 0x70, Tetris::RenderX+Width, 0); + /* + sprintf(buf2, "%-8d", data[0].best_score.first); + sprintf(buf3, "%2d,%d", data[0].best_pos.x, + data[0].best_pos.rot); + ScreenPutString(buf2, 0x08, Tetris::RenderX+Width, 1); + ScreenPutString(buf3, 0x08, Tetris::RenderX+Width, 4); + */ + #endif + return TetrisAIengine::AI_Run(Tetris::Area, seq); + } + virtual int MyKbHit() + { + return PendingAIinput ? 1 : (PendingAIinput = GenerateAIinput()) != 0; + } + virtual int MyGetCh() + { + int r = PendingAIinput; + return r ? (PendingAIinput=0), r : GenerateAIinput(); + } + char d(char c, int maxdelay) + { + if(Timer >= intimer+maxdelay) { intimer=Timer; return c; } + return 0; + } + int GenerateAIinput() + { + /*if(TetrisAIengine::atground) + { + if(seq->r != data[0].best_pos.rot2) return d('w',1); + if(data[0].best_pos.x2 < 0) return d('a', 1); + if(data[0].best_pos.x2 > 0) return d('d', 1); + } + else*/ + { + if(seq->r != data[0].best_pos.rot) return d('w',1); + if(seq->y >= 0 && seq->x > data[0].best_pos.x) return d('a', 2); + if(seq->y >= 0 && seq->x < data[0].best_pos.x) return d('d', 2); + } + if(doubting || !confident) return 0; + return d('s',3); + } + + int PendingAIinput, delay; + unsigned long intimer; + public: + virtual char DelayOpinion() const + { if(doubting || restart || !confident) return -1; + return Tetris::DelayOpinion(); } + + TetrisAI(unsigned rx) : Tetris(rx), PendingAIinput(0), delay(0), intimer(0) {} + }; + #undef Timer +} diff --git a/utils/gen_adldata/file_formats/load_jv.h b/utils/gen_adldata/file_formats/load_jv.h index 125356a..b79832f 100644 --- a/utils/gen_adldata/file_formats/load_jv.h +++ b/utils/gen_adldata/file_formats/load_jv.h @@ -66,7 +66,7 @@ static bool LoadJunglevision(const char *fn, unsigned bank, const char *prefix) tmp[1].data[9] = data[offset + 9 + 11]; tmp[1].data[10] = data[offset + 7 + 11] & 0x0F;//~0x30; tmp[1].finetune = 0; - tmp[1].diff = data[offset]; + tmp[1].diff = (data[offset] != 0); struct ins tmp2; tmp2.notenum = data[offset + 1]; diff --git a/utils/gen_adldata/ini/ini_processing.cpp b/utils/gen_adldata/ini/ini_processing.cpp index 4ef43fc..6e53ba5 100644 --- a/utils/gen_adldata/ini/ini_processing.cpp +++ b/utils/gen_adldata/ini/ini_processing.cpp @@ -416,9 +416,9 @@ bool IniProcessing::parseFile(const char *filename) int newSize = MultiByteToWideChar(CP_UTF8, 0, filename, - dest.size(), + (int)dest.size(), (wchar_t *)dest.c_str(), - dest.size()); + (int)dest.size()); dest.resize(newSize); FILE *cFile = _wfopen(dest.c_str(), L"rb"); #else diff --git a/utils/gen_adldata/measurer.cpp b/utils/gen_adldata/measurer.cpp index 3116045..4789b43 100644 --- a/utils/gen_adldata/measurer.cpp +++ b/utils/gen_adldata/measurer.cpp @@ -220,8 +220,8 @@ DurationInfo MeasureDurations(const ins &in) result.peak_amplitude_time = peak_amplitude_time; result.peak_amplitude_value = peak_amplitude_value; result.begin_amplitude = begin_amplitude; - result.quarter_amplitude_time = quarter_amplitude_time; - result.keyoff_out_time = keyoff_out_time; + result.quarter_amplitude_time = (double)quarter_amplitude_time; + result.keyoff_out_time = (double)keyoff_out_time; result.ms_sound_kon = (long)(quarter_amplitude_time * 1000.0 / interval); result.ms_sound_koff = (long)(keyoff_out_time * 1000.0 / interval); diff --git a/utils/gen_adldata/progs_cache.h b/utils/gen_adldata/progs_cache.h index aa29c8a..7b4cd39 100644 --- a/utils/gen_adldata/progs_cache.h +++ b/utils/gen_adldata/progs_cache.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include -- cgit v1.2.3