From 52bdbc67246e55910e6ed1b4fe2c31d438969f48 Mon Sep 17 00:00:00 2001 From: Nick Black Date: Sun, 22 Mar 2020 17:33:08 -0400 Subject: [PATCH 1/7] ncplane_translate() accept NULL dest as standard plane #408 (#411) * tetris man page * tetris basic skeleton * tetris: Ticker() * README: fix up some obsolete terminology * tetris: draw the game board * tetris: add NewPiece() * tetris: draw tetriminos * tetris: check for stuck piece, move it down * Accept NULL dst in ncplane_translate() #408 --- CMakeLists.txt | 22 ++++ doc/man/index.html | 1 + doc/man/man1/notcurses-tetris.1.md | 32 +++++ doc/man/man3/notcurses_ncplane.3.md | 8 ++ include/ncpp/Cell.hh | 2 +- include/ncpp/Plane.hh | 17 +-- include/notcurses/notcurses.h | 2 +- src/lib/fill.c | 2 +- src/lib/internal.h | 1 - src/lib/notcurses.c | 3 + src/lib/render.c | 3 + src/tetris/main.cpp | 189 ++++++++++++++++++++++++++++ 12 files changed, 271 insertions(+), 11 deletions(-) create mode 100644 doc/man/man1/notcurses-tetris.1.md create mode 100644 src/tetris/main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 18804c24c..7180da5fd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -448,6 +448,27 @@ target_compile_definitions(notcurses-ncreel FORTIFY_SOURCE=2 ) +file(GLOB TETRISSRC CONFIGURE_DEPENDS src/tetris/*.cpp) +add_executable(notcurses-tetris ${TETRISSRC}) +target_include_directories(notcurses-tetris + PRIVATE + include + "${PROJECT_BINARY_DIR}/include" +) +target_link_libraries(notcurses-tetris + PRIVATE + Threads::Threads + notcurses++ +) +target_compile_options(notcurses-tetris + PRIVATE + -Wall -Wextra -W -Wshadow ${DEBUG_OPTIONS} +) +target_compile_definitions(notcurses-tetris + PRIVATE + FORTIFY_SOURCE=2 +) + # notcurses-view file(GLOB VIEWSRCS CONFIGURE_DEPENDS src/view/*.cpp) if(${USE_FFMPEG}) @@ -682,6 +703,7 @@ install(TARGETS notcurses-demo DESTINATION bin) install(TARGETS notcurses-input DESTINATION bin) install(TARGETS notcurses-ncreel DESTINATION bin) install(TARGETS notcurses-tester DESTINATION bin) +install(TARGETS notcurses-tetris DESTINATION bin) if(${USE_FFMPEG}) install(TARGETS notcurses-view DESTINATION bin) endif() diff --git a/doc/man/index.html b/doc/man/index.html index 99bc1f7d6..1315690b1 100644 --- a/doc/man/index.html +++ b/doc/man/index.html @@ -22,6 +22,7 @@ notcurses-ncreel—experiments with ncreels
notcurses-pydemo—validates the Python wrappers
notcurses-tester—unit test driver
+ notcurses-tetris—Tetris in the terminal
notcurses-view—renders images and video to the terminal

C library (section 3)

notcurses_cell—operations on cell objects
diff --git a/doc/man/man1/notcurses-tetris.1.md b/doc/man/man1/notcurses-tetris.1.md new file mode 100644 index 000000000..04d92af10 --- /dev/null +++ b/doc/man/man1/notcurses-tetris.1.md @@ -0,0 +1,32 @@ +% notcurses-tetris(1) +% nick black +% v1.2.3 + +# NAME + +notcurses-tetris - Render images and video to the console + +# SYNOPSIS + +**notcurses-tetris** [**-h|--help**] [**-l loglevel**] + +# DESCRIPTION + +**notcurses-tetris** implements Tetris using notcurses. + +# OPTIONS + +**-h**: Show help and exit. + +**-l loglevel**: Log everything (high log level) or nothing (log level 0) to stderr. + +# NOTES + +Optimal display requires a terminal advertising the **rgb** terminfo(5) +capability, or that the environment variable **COLORTERM** is defined to +**24bit** (and that the terminal honors this variable), along with a +fixed-width font with good coverage of the Unicode Block Drawing Characters. + +# SEE ALSO + +**notcurses(3)** diff --git a/doc/man/man3/notcurses_ncplane.3.md b/doc/man/man3/notcurses_ncplane.3.md index 6be4b6973..a52a400a8 100644 --- a/doc/man/man3/notcurses_ncplane.3.md +++ b/doc/man/man3/notcurses_ncplane.3.md @@ -151,6 +151,14 @@ It is an error for two threads to concurrently access a single ncplane. So long as rendering is not taking place, however, multiple threads may safely output to multiple ncplanes. +**ncplane_translate** translates coordinates expressed relative to the plane +**src**, and writes the coordinates of that cell relative to **dst**. The cell +need not intersect with **dst**, though this will yield coordinates which are +invalid for writing or reading on **dst**. If **dst** is **NULL**, it is taken +to refer to the standard plane. **ncplane_translate_abs** takes coordinates +expressed relative to the standard plane, and returns coordinates relative to +**dst**, returning **false** if the coordinates are invalid for **dst**. + **ncplane_mergedown** writes to **dst** the frame that would be rendered if only **src** and **dst** existed on the z-axis, ad **dst** represented the entirety of the rendering region. Only those cells where **src** intersects with **dst** diff --git a/include/ncpp/Cell.hh b/include/ncpp/Cell.hh index 695a290c7..4f4822fa2 100644 --- a/include/ncpp/Cell.hh +++ b/include/ncpp/Cell.hh @@ -121,7 +121,7 @@ namespace ncpp return cell_simple_p (&_cell); } - uint32_t get_edc_idx () const noexcept + uint32_t get_egc_idx () const noexcept { return cell_egc_idx (&_cell); } diff --git a/include/ncpp/Plane.hh b/include/ncpp/Plane.hh index 792e9baa2..1a7823d07 100644 --- a/include/ncpp/Plane.hh +++ b/include/ncpp/Plane.hh @@ -118,6 +118,14 @@ namespace ncpp return ncplane_pulse (plane, ts, fader, curry) != -1; } + bool mergedown (Plane* dst = nullptr) { + return ncplane_mergedown(*this, dst ? dst->plane : nullptr); + } + + bool mergedown (Plane& dst) { + return mergedown(&dst); + } + bool gradient (const char* egc, uint32_t attrword, uint64_t ul, uint64_t ur, uint64_t ll, uint64_t lr, int ystop, int xstop) const noexcept { return ncplane_gradient (plane, egc, attrword, ul, ur, ll, lr, ystop, xstop) != -1; @@ -860,9 +868,7 @@ namespace ncpp void translate (const Plane *dst, int *y = nullptr, int *x = nullptr) const { - if (dst == nullptr) - throw invalid_argument ("'dst' must be a valid pointer"); - translate (*this, *dst, y, x); + ncplane_translate(*this, dst ? dst->plane: nullptr, y, x); } void translate (const Plane &dst, int *y = nullptr, int *x = nullptr) noexcept @@ -875,10 +881,7 @@ namespace ncpp if (src == nullptr) throw invalid_argument ("'src' must be a valid pointer"); - if (dst == nullptr) - throw invalid_argument ("'dst' must be a valid pointer"); - - translate (*src, *dst, y, x); + ncplane_translate(*src, dst ? dst->plane : nullptr, y, x); } static void translate (const Plane &src, const Plane &dst, int *y = nullptr, int *x = nullptr) noexcept diff --git a/include/notcurses/notcurses.h b/include/notcurses/notcurses.h index f736daca5..e6d1067dd 100644 --- a/include/notcurses/notcurses.h +++ b/include/notcurses/notcurses.h @@ -410,7 +410,7 @@ API struct ncplane* ncplane_dup(struct ncplane* n, void* opaque); // provided a coordinate relative to the origin of 'src', map it to the same // absolute coordinate relative to thte origin of 'dst'. either or both of 'y' -// and 'x' may be NULL. +// and 'x' may be NULL. if 'dst' is NULL, it is taken to be the standard plane. API void ncplane_translate(const struct ncplane* src, const struct ncplane* dst, int* RESTRICT y, int* RESTRICT x); diff --git a/src/lib/fill.c b/src/lib/fill.c index f77dc2491..0d8c1643e 100644 --- a/src/lib/fill.c +++ b/src/lib/fill.c @@ -434,7 +434,7 @@ rotate_output(ncplane* dst, uint32_t tchan, uint32_t bchan){ // // Ideally, rotation through 360 degrees will restore the original 2x1 squre. // Unfortunately, the case where a half block occupies a cell having the same -// fore- and background will see it roated into a single full block. In +// fore- and background will see it rotated into a single full block. In // addition, lower blocks eventually become upper blocks with their channels // reversed. In general: // diff --git a/src/lib/internal.h b/src/lib/internal.h index 624859afc..93c431c4d 100644 --- a/src/lib/internal.h +++ b/src/lib/internal.h @@ -26,7 +26,6 @@ #include #include #include -#include #include "notcurses/notcurses.h" #include "egcpool.h" diff --git a/src/lib/notcurses.c b/src/lib/notcurses.c index 248f9945a..36a3cf343 100644 --- a/src/lib/notcurses.c +++ b/src/lib/notcurses.c @@ -1952,6 +1952,9 @@ bool ncplane_translate_abs(const ncplane* n, int* restrict y, int* restrict x){ void ncplane_translate(const ncplane* src, const ncplane* dst, int* restrict y, int* restrict x){ + if(dst == NULL){ + dst = ncplane_stdplane_const(src); + } if(y){ *y = src->absy - dst->absy + *y; } diff --git a/src/lib/render.c b/src/lib/render.c index c814f39eb..b09866b36 100644 --- a/src/lib/render.c +++ b/src/lib/render.c @@ -382,6 +382,9 @@ postpaint(cell* fb, cell* lastframe, int dimy, int dimx, // paint within the real viewport currently. int ncplane_mergedown(ncplane* restrict src, ncplane* restrict dst){ notcurses* nc = src->nc; + if(dst == NULL){ + dst = nc->stdscr; + } int dimy, dimx; ncplane_dim_yx(dst, &dimy, &dimx); cell* fb = malloc(sizeof(*fb) * dimy * dimx); diff --git a/src/tetris/main.cpp b/src/tetris/main.cpp new file mode 100644 index 000000000..552e6374c --- /dev/null +++ b/src/tetris/main.cpp @@ -0,0 +1,189 @@ +#include +#include +#include +#include +#include +#include +#include + +using namespace std::chrono_literals; + +// "North-facing" tetrimino forms (the form in which they are released from the +// top) are expressed in terms of two rows having between two and four columns. +// We map each game column to four columns and each game row to two rows. +// Each byte of the texture maps to one 4x4 component block (and wastes 7 bits). +static const struct tetrimino { + unsigned color; + const char* texture; +} tetriminos[] = { // OITLJSZ + { 0xcbc900, "****"}, { 0x009caa, " ****"}, { 0x952d98, " * ***"}, + { 0xcf7900, " ****"}, { 0x0065bd, "* ***"}, { 0x69be28, " **** "}, + { 0xbd2939, "** **"} }; + +class TetrisNotcursesErr : public std::runtime_error { +public: + TetrisNotcursesErr(char const* const message) throw() + : std::runtime_error(message) { + } + + virtual char const* what() const throw(){ + return exception::what(); + } +}; + +class Tetris { +public: + Tetris(ncpp::NotCurses& nc) : + nc_(nc), + score_(0), + msdelay_(10ms), + curpiece_(nullptr), + stdplane_(nc_.get_stdplane()) + { + curpiece_ = NewPiece(); + DrawBoard(); + } + + // 0.5 cell aspect: One board height == one row. One board width == two columns. + static constexpr auto BOARD_WIDTH = 10; + static constexpr auto BOARD_HEIGHT = 20; + + // FIXME ideally this would be called from constructor :/ + void Ticker(){ + std::chrono::milliseconds ms; + mtx_.lock(); + do{ + ms = msdelay_; + // FIXME loop and verify we didn't get a spurious wakeup + mtx_.unlock(); + std::this_thread::sleep_for(ms); + mtx_.lock(); + if(curpiece_){ + int y, x; + curpiece_->get_yx(&y, &x); + ++y; + if(PieceStuck()){ + // FIXME lock it into place, get next piece + }else{ + if(!curpiece_->move(y, x) || !nc_.render()){ + // FIXME + } + } + } + }while(ms != std::chrono::milliseconds::zero()); + } + + void Stop(){ + mtx_.lock(); + msdelay_ = std::chrono::milliseconds::zero(); // FIXME wake it up? + mtx_.unlock(); + } + +private: + ncpp::NotCurses& nc_; + uint64_t score_; + std::mutex mtx_; + std::chrono::milliseconds msdelay_; + std::unique_ptr curpiece_; + ncpp::Plane* stdplane_; + + void DrawBoard(){ + int y, x; + stdplane_->get_dim(&y, &x); + uint64_t channels = 0; + channels_set_fg(&channels, 0x00b040); + if(!stdplane_->cursor_move(y - (BOARD_HEIGHT + 2), x / 2 - (BOARD_WIDTH + 1))){ + throw TetrisNotcursesErr("cursor_move()"); + } + if(!stdplane_->rounded_box(0, channels, y - 1, x / 2 + BOARD_WIDTH + 1, NCBOXMASK_TOP)){ + throw TetrisNotcursesErr("rounded_box()"); + } + if(!nc_.render()){ + throw TetrisNotcursesErr("render()"); + } + } + + bool PieceStuck(){ + if(!curpiece_){ + return false; + } + // check for impact. iterate over bottom row of piece's plane, checking for + // presence of glyph. if there, check row below. if row below is occupied, + // we're stuck. + int y, x; + curpiece_->get_dim(&y, &x); + --y; + while(x--){ + int cmpy = y + 1, cmpx = x; // need absolute coordinates via translation + curpiece_->translate(nullptr, &cmpy, &cmpx); + ncpp::Cell c; + auto egc = nc_.get_at(cmpy, cmpx, c); + if(!egc){ + return false; // FIXME is this not indicative of an error? + } + if(*egc && *egc != ' '){ + return true; + } + } + return false; + } + + // tidx is an index into tetriminos. yoff and xoff are relative to the + // terminal's origin. returns colored north-facing tetrimino on a plane. + std::unique_ptr NewPiece(){ + const int tidx = random() % 7; + const struct tetrimino* t = &tetriminos[tidx]; + const size_t cols = strlen(t->texture); + int y, x; + stdplane_->get_dim(&y, &x); + const int xoff = x / 2 - BOARD_WIDTH + (random() % BOARD_WIDTH - 3); + const int yoff = y - (BOARD_HEIGHT + 4); + std::unique_ptr n = std::make_unique(2, cols, yoff, xoff, nullptr); + if(n){ + uint64_t channels = 0; + channels_set_bg_alpha(&channels, CELL_ALPHA_TRANSPARENT); + channels_set_fg_alpha(&channels, CELL_ALPHA_TRANSPARENT); + n->set_fg(t->color); + n->set_base(channels, 0, ""); + y = 0; + for(size_t i = 0 ; i < strlen(t->texture) ; ++i){ + if(t->texture[i] == '*'){ + if(n->putstr(y, x, "██") < 0){ + return NULL; + } + } + y += ((x = ((x + 2) % cols)) == 0); + } + } + return n; + } + +}; + +int main(void){ + if(setlocale(LC_ALL, "") == nullptr){ + return EXIT_FAILURE; + } + notcurses_options ncopts{}; + ncpp::NotCurses nc(ncopts); + Tetris t{nc}; + std::thread tid(&Tetris::Ticker, &t); + char32_t input; + ncinput ni; + while((input = nc.getc(true, &ni)) != (char32_t)-1){ + if(input == 'q'){ + break; + } + switch(input){ + case NCKEY_LEFT: break; + case NCKEY_RIGHT: break; + } + } + if(input == 'q'){ + t.Stop(); + tid.join(); + }else{ + return EXIT_FAILURE; + } + return nc.stop() ? EXIT_SUCCESS : EXIT_FAILURE; +} From ad93c038511f17b5b163e368f3613a63a639f1dd Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Sat, 21 Mar 2020 22:35:41 +0100 Subject: [PATCH 2/7] [C++] API sync Added: * Plane: mergedown (`ncplane_mergedown`) --- include/ncpp/Plane.hh | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/include/ncpp/Plane.hh b/include/ncpp/Plane.hh index 1a7823d07..ac053fda0 100644 --- a/include/ncpp/Plane.hh +++ b/include/ncpp/Plane.hh @@ -303,6 +303,20 @@ namespace ncpp return move_above (*above); } + bool mergedown (Plane &dst) const + { + if (plane == dst.plane) + throw invalid_argument ("'dst' must refer to a differnt plane than the one this method is called on"); + return ncplane_mergedown (plane, dst.plane) != -1; + } + + bool mergedown (Plane *dst) const + { + if (dst == nullptr) + throw invalid_argument ("'dst' must be a valid pointer"); + return mergedown (*dst); + } + bool cursor_move (int y, int x) const noexcept { return ncplane_cursor_move_yx (plane, y, x) != -1; From 3fff41ffa28dcd3d2a9240ec24964f9ffa9cfc14 Mon Sep 17 00:00:00 2001 From: nick black Date: Sun, 22 Mar 2020 18:06:43 -0400 Subject: [PATCH 3/7] Doxygen: update INPUT for include hierarchy --- doc/Doxyfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/Doxyfile b/doc/Doxyfile index 2e8b41f84..618e08312 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -829,7 +829,7 @@ WARN_LOGFILE = # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -INPUT = ../src/lib ../include/notcurses.h +INPUT = ../src/lib ../include # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses From c3feca6dedd9b0bf31edd95bcfc0cba71f85be65 Mon Sep 17 00:00:00 2001 From: nick black Date: Sun, 22 Mar 2020 23:09:52 -0400 Subject: [PATCH 4/7] ncplane_polyfill: reject null glyph + test #418 --- src/lib/fill.c | 8 +++++--- tests/fills.cpp | 8 ++++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/lib/fill.c b/src/lib/fill.c index 0d8c1643e..6f95077d8 100644 --- a/src/lib/fill.c +++ b/src/lib/fill.c @@ -57,9 +57,11 @@ ncplane_polyfill_recurse(ncplane* n, int y, int x, const cell* c){ // at the initial step only, invalid y, x is an error, so explicitly check. int ncplane_polyfill_yx(ncplane* n, int y, int x, const cell* c){ int ret = -1; - if(y < n->leny && x < n->lenx){ - if(y >= 0 && x >= 0){ - ret = ncplane_polyfill_recurse(n, y, x, c); + if(c->gcluster){ // can't polyfill with a null EGC + if(y < n->leny && x < n->lenx){ + if(y >= 0 && x >= 0){ + ret = ncplane_polyfill_recurse(n, y, x, c); + } } } return ret; diff --git a/tests/fills.cpp b/tests/fills.cpp index 945829be8..13900d718 100644 --- a/tests/fills.cpp +++ b/tests/fills.cpp @@ -17,6 +17,14 @@ TEST_CASE("Fills") { struct ncplane* n_ = notcurses_stdplane(nc_); REQUIRE(n_); + // can't polyfill with a null glyph + SUBCASE("PolyfillNullGlyph") { + int dimx, dimy; + ncplane_dim_yx(n_, &dimy, &dimx); + cell c = CELL_TRIVIAL_INITIALIZER; + CHECK(0 > ncplane_polyfill_yx(n_, dimy, dimx, &c)); + } + // trying to polyfill an invalid cell ought be an error SUBCASE("PolyfillOffplane") { int dimx, dimy; From 2b7b384e9100fdf441661ded3770718a83d6d14f Mon Sep 17 00:00:00 2001 From: Nick Black Date: Mon, 23 Mar 2020 01:12:09 -0400 Subject: [PATCH 5/7] Most of notcurses-tetris, mergedown fixes (#420) * ncplane_mergedown() fix for small planes #417 * tetris game needed for the book --- data/tetris-background.jpeg | Bin 0 -> 78983 bytes include/notcurses/notcurses.h | 17 +-- src/lib/internal.h | 15 ++- src/lib/render.c | 70 ++++++----- src/tetris/main.cpp | 221 ++++++++++++++++++++++++++++------ tests/fills.cpp | 89 +++++++++++++- 6 files changed, 333 insertions(+), 79 deletions(-) create mode 100644 data/tetris-background.jpeg diff --git a/data/tetris-background.jpeg b/data/tetris-background.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..e7df7d3ad5d88e797718d2ad6097ed5bc6ffa89b GIT binary patch literal 78983 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 AN>2O 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.poolsize); + for(int y = 0 ; y < 1 ; ++y){ + for(int x = 0 ; x < 10 ; ++x){ + const cell* c = &n->fb[fbcellidx(y, dimx, x)]; + fprintf(stderr, "[%03d/%03d] ", y, x); + cell_debug(&n->pool, c); + } + } +} + // True if the cell does not generate background pixels. Only the FULL BLOCK // glyph has this property, AFAIK. // FIXME set a bit, doing this at load time @@ -571,7 +585,6 @@ cell_duplicate_far(egcpool* tpool, cell* targ, const ncplane* splane, const cell return !!c->gcluster; } size_t ulen = strlen(extended_gcluster(splane, c)); -//fprintf(stderr, "[%s] (%zu)\n", egcpool_extended_gcluster(&splane->pool, c), strlen(egcpool_extended_gcluster(&splane->pool, c))); int eoffset = egcpool_stash(tpool, extended_gcluster(splane, c), ulen); if(eoffset < 0){ return -1; diff --git a/src/lib/render.c b/src/lib/render.c index b09866b36..92dbd1ae9 100644 --- a/src/lib/render.c +++ b/src/lib/render.c @@ -203,19 +203,21 @@ lock_in_highcontrast(cell* targc, struct crender* crender){ } } -// Paints a single ncplane into the provided framebuffer 'fb'. Whenever a cell -// is locked in, it is compared against the last frame. If it is different, the -// 'damagevec' bitmap is updated with a 1. 'pool' is typically nc->pool, but can +// Paints a single ncplane into the provided scratch framebuffer 'fb', and +// ultimately 'lastframe' (we can't always write directly into 'lastframe', +// because we need build state to solve certain cells, and need compare their +// solved result to the last frame). Whenever a cell is locked in, it is +// compared against the last frame. If it is different, the 'rvec' bitmap is updated with a 1. 'pool' is typically nc->pool, but can // be whatever's backing fb. static int -paint(notcurses* nc, ncplane* p, cell* lastframe, struct crender* rvec, cell* fb, egcpool* pool){ +paint(ncplane* p, cell* lastframe, struct crender* rvec, + cell* fb, egcpool* pool, int dstleny, int dstlenx, + int dstabsy, int dstabsx, int lfdimx){ int y, x, dimy, dimx, offy, offx; - // don't use ncplane_dim_yx()/ncplane_yx() here, lest we deadlock - dimy = p->leny; - dimx = p->lenx; - offy = p->absy - nc->stdscr->absy; - offx = p->absx - nc->stdscr->absx; -//fprintf(stderr, "PLANE %p %d %d %d %d %d %d\n", p, dimy, dimx, offy, offx, nc->stdscr->leny, nc->stdscr->lenx); + ncplane_dim_yx(p, &dimy, &dimx); + offy = p->absy - dstabsy; + offx = p->absx - dstabsx; +//fprintf(stderr, "PLANE %p %d %d %d %d %d %d\n", p, dimy, dimx, offy, offx, dstleny, dstlenx); // skip content above or to the left of the physical screen int starty, startx; if(offy < 0){ @@ -231,19 +233,19 @@ paint(notcurses* nc, ncplane* p, cell* lastframe, struct crender* rvec, cell* fb for(y = starty ; y < dimy ; ++y){ const int absy = y + offy; // once we've passed the physical screen's bottom, we're done - if(absy >= nc->stdscr->leny){ + if(absy >= dstleny){ break; } for(x = startx ; x < dimx ; ++x){ const int absx = x + offx; - if(absx >= nc->stdscr->lenx){ + if(absx >= dstlenx){ break; } - cell* targc = &fb[fbcellidx(absy, nc->stdscr->lenx, absx)]; + cell* targc = &fb[fbcellidx(absy, dstlenx, absx)]; if(cell_locked_p(targc)){ continue; } - struct crender* crender = &rvec[fbcellidx(absy, nc->stdscr->lenx, absx)]; + struct crender* crender = &rvec[fbcellidx(absy, dstlenx, absx)]; const cell* vis = &p->fb[nfbcellidx(p, y, x)]; // if we never loaded any content into the cell (or obliterated it by // writing in a zero), use the plane's base cell. @@ -263,7 +265,7 @@ paint(notcurses* nc, ncplane* p, cell* lastframe, struct crender* rvec, cell* fb // screen, nor if we're bisected by a higher plane. if(cell_double_wide_p(vis)){ // are we on the last column of the real screen? if so, 0x20 us - if(absx >= nc->stdscr->lenx - 1){ + if(absx >= dstlenx - 1){ targc->gcluster = ' '; // is the next cell occupied? if so, 0x20 us }else if(targc[1].gcluster){ @@ -313,13 +315,14 @@ paint(notcurses* nc, ncplane* p, cell* lastframe, struct crender* rvec, cell* fb // which were already locked in were skipped at the top of the loop)? if(cell_locked_p(targc)){ lock_in_highcontrast(targc, crender); - cell* prevcell = &lastframe[fbcellidx(absy, nc->lfdimx, absx)]; + cell* prevcell = &lastframe[fbcellidx(absy, lfdimx, absx)]; /*if(cell_simple_p(targc)){ fprintf(stderr, "WROTE %u [%c] to %d/%d (%d/%d)\n", targc->gcluster, prevcell->gcluster, y, x, absy, absx); }else{ fprintf(stderr, "WROTE %u [%s] to %d/%d (%d/%d)\n", targc->gcluster, extended_gcluster(crender->p, targc), y, x, absy, absx); } -fprintf(stderr, "POOL: %p NC: %p SRC: %p\n", nc->pool.pool, nc, crender->p);*/ +fprintf(stderr, "POOL: %p NC: %p SRC: %p\n", pool->pool, nc, crender->p); +}*/ if(cellcmp_and_dupfar(pool, prevcell, crender->p, targc)){ crender->damaged = true; if(cell_double_wide_p(targc)){ @@ -387,19 +390,30 @@ int ncplane_mergedown(ncplane* restrict src, ncplane* restrict dst){ } int dimy, dimx; ncplane_dim_yx(dst, &dimy, &dimx); - cell* fb = malloc(sizeof(*fb) * dimy * dimx); + cell* tmpfb = malloc(sizeof(*tmpfb) * dimy * dimx); + cell* rendfb = malloc(sizeof(*rendfb) * dimy * dimx); const size_t crenderlen = sizeof(struct crender) * dimy * dimx; struct crender* rvec = malloc(crenderlen); memset(rvec, 0, crenderlen); - init_fb(fb, dimy, dimx); - if(paint(nc, src, dst->fb, rvec, fb, &dst->pool) || paint(nc, dst, dst->fb, rvec, fb, &dst->pool)){ + init_fb(tmpfb, dimy, dimx); + init_fb(rendfb, dimy, dimx); + if(paint(src, rendfb, rvec, tmpfb, &dst->pool, dst->leny, dst->lenx, + dst->absy, dst->absx, dst->lenx)){ + free(rvec); + free(rendfb); + free(tmpfb); + return -1; + } + if(paint(dst, rendfb, rvec, tmpfb, &dst->pool, dst->leny, dst->lenx, + dst->absy, dst->absx, dst->lenx)){ free(rvec); - free(fb); + free(rendfb); + free(tmpfb); return -1; } - postpaint(fb, dst->fb, dimy, dimx, rvec, &dst->pool); + postpaint(tmpfb, rendfb, dimy, dimx, rvec, &dst->pool); free(dst->fb); - dst->fb = fb; + dst->fb = rendfb; free(rvec); return 0; } @@ -420,7 +434,9 @@ notcurses_render_internal(notcurses* nc, struct crender* rvec){ init_fb(fb, dimy, dimx); ncplane* p = nc->top; while(p){ - if(paint(nc, p, nc->lastframe, rvec, fb, &nc->pool)){ + if(paint(p, nc->lastframe, rvec, fb, &nc->pool, + nc->stdscr->leny, nc->stdscr->lenx, + nc->stdscr->absy, nc->stdscr->absx, nc->lfdimx)){ free(fb); return -1; } @@ -790,9 +806,9 @@ update_palette(notcurses* nc, FILE* out){ // * refresh -- write the stream to the emulator // Takes a rendered frame (a flat framebuffer, where each cell has the desired -// EGC, attribute, and channels) and the previously-rendered frame, and spits -// out an optimal sequence of terminal-appropriate escapes and EGCs. There -// should be an rvec entry for each cell; only the 'damaged' field is used. +// EGC, attribute, and channels), which has been written to nc->lastframe, and +// spits out an optimal sequence of terminal-appropriate escapes and EGCs. There +// should be an rvec entry for each cell, but only the 'damaged' field is used. static int notcurses_rasterize(notcurses* nc, const struct crender* rvec){ FILE* out = nc->rstate.mstreamfp; diff --git a/src/tetris/main.cpp b/src/tetris/main.cpp index 552e6374c..f054835fa 100644 --- a/src/tetris/main.cpp +++ b/src/tetris/main.cpp @@ -1,11 +1,13 @@ -#include #include +#include #include #include #include #include #include +const std::string BackgroundFile = "../data/tetris-background.jpeg"; + using namespace std::chrono_literals; // "North-facing" tetrimino forms (the form in which they are released from the @@ -22,26 +24,31 @@ static const struct tetrimino { class TetrisNotcursesErr : public std::runtime_error { public: + TetrisNotcursesErr(const std::string& s) throw() + : std::runtime_error(s) { + } TetrisNotcursesErr(char const* const message) throw() : std::runtime_error(message) { } - - virtual char const* what() const throw(){ + virtual char const* what() const throw() { return exception::what(); } }; class Tetris { public: - Tetris(ncpp::NotCurses& nc) : + Tetris(ncpp::NotCurses& nc, std::atomic_bool& gameover) : nc_(nc), score_(0), - msdelay_(10ms), + msdelay_(100ms), curpiece_(nullptr), - stdplane_(nc_.get_stdplane()) + board_(nullptr), + backg_(nullptr), + stdplane_(nc_.get_stdplane()), + gameover_(gameover) { - curpiece_ = NewPiece(); DrawBoard(); + curpiece_ = NewPiece(); } // 0.5 cell aspect: One board height == one row. One board width == two columns. @@ -49,7 +56,7 @@ public: static constexpr auto BOARD_HEIGHT = 20; // FIXME ideally this would be called from constructor :/ - void Ticker(){ + void Ticker() { std::chrono::milliseconds ms; mtx_.lock(); do{ @@ -57,26 +64,117 @@ public: // FIXME loop and verify we didn't get a spurious wakeup mtx_.unlock(); std::this_thread::sleep_for(ms); - mtx_.lock(); + const std::lock_guard lock(mtx_); if(curpiece_){ int y, x; curpiece_->get_yx(&y, &x); - ++y; if(PieceStuck()){ - // FIXME lock it into place, get next piece + if(y <= board_top_y_ - 2){ + gameover_ = true; + return; + } + curpiece_->mergedown(*board_); + curpiece_ = NewPiece(); }else{ + ++y; if(!curpiece_->move(y, x) || !nc_.render()){ - // FIXME + throw TetrisNotcursesErr("move() or render()"); } } } - }while(ms != std::chrono::milliseconds::zero()); + }while(!gameover_); } - void Stop(){ - mtx_.lock(); - msdelay_ = std::chrono::milliseconds::zero(); // FIXME wake it up? - mtx_.unlock(); + void MoveLeft() { + const std::lock_guard lock(mtx_); + int y, x; + if(!PrepForMove(&y, &x)){ + return; + } + // For each line of the current piece, find the leftmost populated column. + // Check the game area to the immediate left. If something's there, we + // can't make this move. + ncpp::Cell c; + for(int ly = 0 ; ly < curpiece_->get_dim_y() ; ++ly){ + int lx = 0; + while(lx < curpiece_->get_dim_x()){ + if(curpiece_->get_at(ly, lx, &c)){ + if(c.get().gcluster && c.get().gcluster != ' '){ + break; + } + } + ++lx; + } + if(lx < curpiece_->get_dim_x()){ // otherwise, nothing on this row + ncpp::Cell b; + int cmpy = ly, cmpx = lx - 1; + curpiece_->translate(*board_, &cmpy, &cmpx); + if(board_->get_at(cmpy, cmpx, &b)){ + if(b.get().gcluster && b.get().gcluster != ' '){ + return; // move is blocked + } + } + } + } + --x; + if(!curpiece_->move(y, x) || !nc_.render()){ // FIXME needs y? + throw TetrisNotcursesErr("move() or render()"); + } + } + + void MoveRight() { + const std::lock_guard lock(mtx_); + int y, x; + if(!PrepForMove(&y, &x)){ + return; + } + // For each line of the current piece, find the rightmost populated column. + // Check the game area to the immediate right. If something's there, we + // can't make this move. + ncpp::Cell c; + for(int ly = 0 ; ly < curpiece_->get_dim_y() ; ++ly){ + int lx = curpiece_->get_dim_x() - 1; + while(lx >= 0){ + if(curpiece_->get_at(ly, lx, &c)){ + if(c.get().gcluster && c.get().gcluster != ' '){ + break; + } + } + --lx; + } + if(lx >= 0){ // otherwise, nothing on this row + ncpp::Cell b; + int cmpy = ly, cmpx = lx + 1; + curpiece_->translate(*board_, &cmpy, &cmpx); + if(board_->get_at(cmpy, cmpx, &b)){ + if(b.get().gcluster && b.get().gcluster != ' '){ + return; // move is blocked + } + } + } + } + ++x; + if(!curpiece_->move(y, x) || !nc_.render()){ // FIXME needs y? + throw TetrisNotcursesErr("move() or render()"); + } + } + + void RotateCcw() { + const std::lock_guard lock(mtx_); + int y, x; + if(!PrepForMove(&y, &x)){ + return; + } + // FIXME rotate that fucker ccw + } + + void RotateCw() { + const std::lock_guard lock(mtx_); + int y, x; + if(!PrepForMove(&y, &x)){ + return; + } + // FIXME rotate that fucker cw } private: @@ -85,25 +183,60 @@ private: std::mutex mtx_; std::chrono::milliseconds msdelay_; std::unique_ptr curpiece_; + std::unique_ptr board_; + std::unique_ptr backg_; ncpp::Plane* stdplane_; + std::atomic_bool& gameover_; + int board_top_y_; + + // Returns true if there's a current piece which can be moved + bool PrepForMove(int* y, int* x) { + if(!curpiece_){ + return false; + } + curpiece_->get_yx(y, x); + return true; + } - void DrawBoard(){ + // background is drawn to the standard plane, at the bottom. + void DrawBackground(const std::string& s) { + int averr; + try{ + backg_ = std::make_unique(s.c_str(), &averr, 0, 0, ncpp::NCScale::Stretch); + }catch(std::exception& e){ + throw TetrisNotcursesErr("visual(): " + s + ": " + e.what()); + } + if(!backg_->decode(&averr)){ + throw TetrisNotcursesErr("decode(): " + s); + } + if(!backg_->render(0, 0, 0, 0)){ + throw TetrisNotcursesErr("render(): " + s); + } + } + + // draw the background on the standard plane, then create a new plane for + // the play area. + void DrawBoard() { + DrawBackground(BackgroundFile); int y, x; stdplane_->get_dim(&y, &x); + board_top_y_ = y - (BOARD_HEIGHT + 2); + board_ = std::make_unique(BOARD_HEIGHT, BOARD_WIDTH * 2, + board_top_y_, x / 2 - (BOARD_WIDTH + 1)); uint64_t channels = 0; channels_set_fg(&channels, 0x00b040); - if(!stdplane_->cursor_move(y - (BOARD_HEIGHT + 2), x / 2 - (BOARD_WIDTH + 1))){ - throw TetrisNotcursesErr("cursor_move()"); - } - if(!stdplane_->rounded_box(0, channels, y - 1, x / 2 + BOARD_WIDTH + 1, NCBOXMASK_TOP)){ + channels_set_bg_alpha(&channels, CELL_ALPHA_TRANSPARENT); + if(!board_->rounded_box(0, channels, BOARD_HEIGHT - 1, BOARD_WIDTH * 2 - 1, NCBOXMASK_TOP)){ throw TetrisNotcursesErr("rounded_box()"); } + channels_set_fg_alpha(&channels, CELL_ALPHA_TRANSPARENT); + board_->set_base(channels, 0, ""); if(!nc_.render()){ throw TetrisNotcursesErr("render()"); } } - bool PieceStuck(){ + bool PieceStuck() { if(!curpiece_){ return false; } @@ -114,14 +247,13 @@ private: curpiece_->get_dim(&y, &x); --y; while(x--){ - int cmpy = y + 1, cmpx = x; // need absolute coordinates via translation - curpiece_->translate(nullptr, &cmpy, &cmpx); + int cmpy = y + 1, cmpx = x; // need game area coordinates via translation + curpiece_->translate(*board_, &cmpy, &cmpx); ncpp::Cell c; - auto egc = nc_.get_at(cmpy, cmpx, c); - if(!egc){ - return false; // FIXME is this not indicative of an error? + if(board_->get_at(cmpy, cmpx, &c) < 0){ + throw TetrisNotcursesErr("get_at()"); } - if(*egc && *egc != ' '){ + if(c.get().gcluster && c.get().gcluster != ' '){ return true; } } @@ -130,20 +262,20 @@ private: // tidx is an index into tetriminos. yoff and xoff are relative to the // terminal's origin. returns colored north-facing tetrimino on a plane. - std::unique_ptr NewPiece(){ + std::unique_ptr NewPiece() { const int tidx = random() % 7; const struct tetrimino* t = &tetriminos[tidx]; const size_t cols = strlen(t->texture); int y, x; stdplane_->get_dim(&y, &x); - const int xoff = x / 2 - BOARD_WIDTH + (random() % BOARD_WIDTH - 3); - const int yoff = y - (BOARD_HEIGHT + 4); - std::unique_ptr n = std::make_unique(2, cols, yoff, xoff, nullptr); + const int xoff = x / 2 - BOARD_WIDTH + (random() % BOARD_WIDTH - 1); + std::unique_ptr n = std::make_unique(2, cols, board_top_y_ - 2, xoff, nullptr); if(n){ uint64_t channels = 0; channels_set_bg_alpha(&channels, CELL_ALPHA_TRANSPARENT); channels_set_fg_alpha(&channels, CELL_ALPHA_TRANSPARENT); n->set_fg(t->color); + n->set_bg_alpha(CELL_ALPHA_TRANSPARENT); n->set_base(channels, 0, ""); y = 0; for(size_t i = 0 ; i < strlen(t->texture) ; ++i){ @@ -160,27 +292,36 @@ private: }; -int main(void){ +int main(void) { if(setlocale(LC_ALL, "") == nullptr){ return EXIT_FAILURE; } + std::atomic_bool gameover = false; notcurses_options ncopts{}; ncpp::NotCurses nc(ncopts); - Tetris t{nc}; + Tetris t{nc, gameover}; std::thread tid(&Tetris::Ticker, &t); + ncpp::Plane* stdplane = nc.get_stdplane(); char32_t input; ncinput ni; - while((input = nc.getc(true, &ni)) != (char32_t)-1){ + while(!gameover && (input = nc.getc(true, &ni)) != (char32_t)-1){ if(input == 'q'){ break; } switch(input){ - case NCKEY_LEFT: break; - case NCKEY_RIGHT: break; + case NCKEY_LEFT: t.MoveLeft(); break; + case NCKEY_RIGHT: t.MoveRight(); break; + case 'z': t.RotateCcw(); break; + case 'x': t.RotateCw(); break; + default: + stdplane->cursor_move(0, 0); + stdplane->printf("Got unknown input U+%06x", input); + nc.render(); + break; } } - if(input == 'q'){ - t.Stop(); + if(gameover || input == 'q'){ + gameover = true; tid.join(); }else{ return EXIT_FAILURE; diff --git a/tests/fills.cpp b/tests/fills.cpp index 13900d718..32c099394 100644 --- a/tests/fills.cpp +++ b/tests/fills.cpp @@ -48,11 +48,17 @@ TEST_CASE("Fills") { CHECK(0 == ncplane_destroy(pfn)); } + SUBCASE("PolyfillStandardPlane") { + cell c = CELL_SIMPLE_INITIALIZER('-'); + CHECK(0 < ncplane_polyfill_yx(n_, 0, 0, &c)); + CHECK(0 == notcurses_render(nc_)); + } + SUBCASE("PolyfillEmptyPlane") { cell c = CELL_SIMPLE_INITIALIZER('+'); - struct ncplane* pfn = ncplane_new(nc_, 4, 4, 0, 0, nullptr); + struct ncplane* pfn = ncplane_new(nc_, 20, 20, 0, 0, nullptr); REQUIRE(nullptr != pfn); - CHECK(16 == ncplane_polyfill_yx(pfn, 0, 0, &c)); + CHECK(400 == ncplane_polyfill_yx(pfn, 0, 0, &c)); CHECK(0 == notcurses_render(nc_)); CHECK(0 == ncplane_destroy(pfn)); } @@ -319,7 +325,7 @@ TEST_CASE("Fills") { CHECK(0 == notcurses_render(nc_)); } - SUBCASE("MergeDown") { + SUBCASE("MergeDownASCII") { auto p1 = ncplane_new(nc_, 1, 10, 0, 0, nullptr); REQUIRE(p1); // make sure glyphs replace nulls @@ -353,6 +359,83 @@ TEST_CASE("Fills") { ncplane_destroy(p1); } + SUBCASE("MergeDownUni") { + auto p1 = ncplane_new(nc_, 1, 10, 0, 0, nullptr); + REQUIRE(p1); + // make sure glyphs replace nulls + CHECK(0 < ncplane_putstr(p1, "█▀▄▌▐🞵🞶🞷🞸🞹")); + CHECK(0 == ncplane_mergedown(p1, n_)); + cell cbase = CELL_TRIVIAL_INITIALIZER; + cell cp = CELL_TRIVIAL_INITIALIZER; + for(int i = 0 ; i < 10 ; ++i){ + CHECK(0 < ncplane_at_yx(n_, 0, i, &cbase)); + CHECK(0 < ncplane_at_yx(p1, 0, i, &cp)); + CHECK(0 == cellcmp(n_, &cbase, p1, &cp)); + } + ncplane_destroy(p1); + CHECK(0 == notcurses_render(nc_)); + auto p3 = ncplane_new(nc_, 1, 10, 0, 0, nullptr); + CHECK(0 == ncplane_cursor_move_yx(p3, 0, 0)); + // make sure glyphs replace glyps + CHECK(0 < ncplane_putstr(p3, "🞵🞶🞷🞸🞹█▀▄▌▐")); + CHECK(0 == ncplane_mergedown(p3, NULL)); + cell c3 = CELL_TRIVIAL_INITIALIZER; + for(int i = 0 ; i < 10 ; ++i){ + CHECK(0 < ncplane_at_yx(n_, 0, i, &cbase)); + CHECK(0 < ncplane_at_yx(p3, 0, i, &c3)); + CHECK(0 == cellcmp(n_, &cbase, p3, &c3)); + } + CHECK(0 == notcurses_render(nc_)); + // make sure nulls do not replace glyphs + auto p2 = ncplane_new(nc_, 1, 10, 0, 0, nullptr); + CHECK(0 == ncplane_mergedown(p2, NULL)); + ncplane_destroy(p2); + for(int i = 0 ; i < 10 ; ++i){ + CHECK(0 < ncplane_at_yx(n_, 0, i, &cbase)); + CHECK(0 < ncplane_at_yx(p3, 0, i, &c3)); + CHECK(0 == cellcmp(n_, &cbase, p3, &c3)); + } + ncplane_destroy(p3); + CHECK(0 == notcurses_render(nc_)); + } + + // test merging down one plane to another plane which is smaller than the + // standard plane + SUBCASE("MergeDownSmallPlane") { + constexpr int DIMX = 10; + constexpr int DIMY = 10; + auto p1 = ncplane_new(nc_, DIMY, DIMX, 2, 2, nullptr); + REQUIRE(p1); + cell c1 = CELL_TRIVIAL_INITIALIZER; + CHECK(0 < cell_load(p1, &c1, "█")); + CHECK(0 == cell_set_bg(&c1, 0x00ff00)); + CHECK(0 == cell_set_fg(&c1, 0x0000ff)); + ncplane_polyfill_yx(p1, 0, 0, &c1); + CHECK(0 == notcurses_render(nc_)); + auto p2 = ncplane_new(nc_, DIMY / 2, DIMX / 2, 3, 3, nullptr); + REQUIRE(p2); + cell c2 = CELL_TRIVIAL_INITIALIZER; + CHECK(0 < cell_load(p2, &c2, "🞶")); + CHECK(0 == cell_set_bg(&c2, 0x00ffff)); + CHECK(0 == cell_set_fg(&c2, 0xff00ff)); + ncplane_polyfill_yx(p2, 0, 0, &c2); + CHECK(0 == ncplane_mergedown(p2, p1)); + CHECK(0 == notcurses_render(nc_)); + for(int y = 0 ; y < DIMY ; ++y){ + for(int x = 0 ; x < DIMX ; ++x){ + CHECK(0 < ncplane_at_yx(p1, y, x, &c1)); + if(y < 1 || y > 5 || x < 1 || x > 5){ + CHECK(0 == strcmp(extended_gcluster(p1, &c1), "█")); + }else{ + CHECK(0 < ncplane_at_yx(p2, y - 1, x - 1, &c2)); + CHECK(0 == cellcmp(p1, &c1, p2, &c2)); + } + } + } + ncplane_destroy(p1); + ncplane_destroy(p2); + } + CHECK(0 == notcurses_stop(nc_)); CHECK(0 == fclose(outfp_)); From 84ef40143ee66c970b58e1249e30b00622331f3f Mon Sep 17 00:00:00 2001 From: nick black Date: Mon, 23 Mar 2020 01:13:25 -0400 Subject: [PATCH 6/7] tetris: eliminate compiler warning --- src/tetris/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tetris/main.cpp b/src/tetris/main.cpp index f054835fa..8a3ef153b 100644 --- a/src/tetris/main.cpp +++ b/src/tetris/main.cpp @@ -302,7 +302,7 @@ int main(void) { Tetris t{nc, gameover}; std::thread tid(&Tetris::Ticker, &t); ncpp::Plane* stdplane = nc.get_stdplane(); - char32_t input; + char32_t input = 0; ncinput ni; while(!gameover && (input = nc.getc(true, &ni)) != (char32_t)-1){ if(input == 'q'){ From 8113ce72c102f7302d5b4e615ee6b1b20f5417a3 Mon Sep 17 00:00:00 2001 From: nick black Date: Mon, 23 Mar 2020 12:46:35 -0400 Subject: [PATCH 7/7] fix dumb error in ncplane_below() description --- include/notcurses/notcurses.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/notcurses/notcurses.h b/include/notcurses/notcurses.h index e2d567377..ad69d6e2a 100644 --- a/include/notcurses/notcurses.h +++ b/include/notcurses/notcurses.h @@ -556,7 +556,7 @@ ncplane_move_below(struct ncplane* n, struct ncplane* below){ return ncplane_move_below_unsafe(n, below); } -// Return the plane above this one, or NULL if this is at the top. +// Return the plane below this one, or NULL if this is at the bottom. API struct ncplane* ncplane_below(struct ncplane* n); // Rotate the plane pi/2 radians clockwise or counterclockwise. Note that