~muirrum/cpp-rl

36d4f4741cd2559362de7e64820ca4b29b022121 — Cara Salter 1 year, 6 months ago master
Initial commit
A  => .gitignore +68 -0
@@ 1,68 @@
# Created by https://www.toptal.com/developers/gitignore/api/scons,vim,c++
# Edit at https://www.toptal.com/developers/gitignore?templates=scons,vim,c++

### C++ ###
# Prerequisites
*.d

# Compiled Object files
*.slo
*.lo
*.o
*.obj

# Precompiled Headers
*.gch
*.pch

# Compiled Dynamic libraries
*.so
*.dylib
*.dll

# Fortran module files
*.mod
*.smod

# Compiled Static libraries
*.lai
*.la
*.a
*.lib

# Executables
*.exe
*.out
*.app

### SCons ###
# for projects that use SCons for building: http://http://www.scons.org/
.sconsign.dblite

# When configure fails, SCons outputs these
config.log
.sconf_temp

### Vim ###
# Swap
[._]*.s[a-v][a-z]
!*.svg  # comment out if you don't need vector files
[._]*.sw[a-p]
[._]s[a-rt-v][a-z]
[._]ss[a-gi-z]
[._]sw[a-p]

# Session
Session.vim
Sessionx.vim

# Temporary
.netrwhist
*~
# Auto-generated tag files
tags
# Persistent undo
[._]*.un~

# End of https://www.toptal.com/developers/gitignore/api/scons,vim,c++
cpp-rl*

A  => SConstruct +9 -0
@@ 1,9 @@

envDebug = Environment(CXXFLAGS='-g')



targetDebug = envDebug.Program(target = 'cpp-rl-dbg', source=Glob('src/*.cpp'), LIBS=['ncurses', 'tinfo'])

envDebug.Default(targetDebug)


A  => src/frame.cpp +216 -0
@@ 1,216 @@
/*
 * =====================================================================================
 *
 *       Filename:  frame.cpp
 *
 *    Description:  Implementation of frames 
 *
 *        Version:  1.0
 *        Created:  01/07/2023 10:11:54 PM
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  Cara Salter (muirrum), cara@devcara.com
 *   Organization:  
 *
 * =====================================================================================
 */

#include <stdlib.h>
#include "frame.hpp"
#include "noise.hpp"
#include <ncurses.h>

Frame::Frame(int nr_rows, int nr_cols, int row_0, int col_0) {
        _has_super = FALSE;
        _super = NULL;
        _w = newwin(nr_rows, nr_cols, row_0, col_0);
        _height = nr_rows;
        _width = nr_cols;
        _row = row_0;
        _col = col_0;
}

Frame::Frame(Frame &sw, int nr_rows, int nr_cols, int row_0, int col_0) {
    _has_super = TRUE;
    _super = sw.win();

    _w = derwin(sw.win(), nr_rows, nr_cols, row_0, col_0);
    _height = nr_rows;
    _width = nr_cols;
    _row = row_0;
    _col = col_0;
}

Frame::~Frame() {
    delwin(_w);
}

// Place a mob in the window
void Frame::place_mob(Mob &x) {
    mvwaddch(_w, x.row(), x.col(), x.symbol());
}

void Frame::place_mob(Mob &x, int row_0, int col_0) {
    if ((row_0 >= 0 && row_0 < _height) && (col_0 >= 0 && col_0 < _width)) {
        // Get element at target position
        char target = mvwinch(_w, row_0, col_0);
        if (target == '~' || target == '#' || target == 'S') {
            return;
        }
        erase(x);
        mvwaddch(_w, row_0, col_0, x.symbol());
        x.move(row_0, col_0);
    }
}

void Frame::erase(Mob &x) {
    mvwaddch(_w, x.row(), x.col(), ' ');
}

void Frame::center(Mob &x) {
	if(_has_super) {
		int rr = _row, cc = _col, hh, ww;
		int _r = x.row() - _height/2;
		int _c = x.col() - _width/2;

		getmaxyx(_super, hh, ww);

		if(_c + _width >= ww) {
			int delta = ww - (_c + _width);
			cc = _c + delta;
		}
		else {
			cc = _c;
		}

		if(_r +_height >= hh) {
			int delta = hh - (_r +_height);
			rr = _r + delta;
		}
		else {
			rr = _r;
		}

		if (_r < 0) {
			rr = 0;
		}

		if (_c < 0) {
			cc = 0;
		}


		move(rr, cc);
	}
}

// Refresh window
void Frame::refresh() {
    if (_has_super) {
        touchwin(_super);
    }
    wrefresh(_w);
}

// Move a window to a new position
void Frame::move(int r, int c) {
    if (_has_super) {
        mvderwin(_w, r, c);
        _row = r;
        _col = c;
        ::refresh();
    }
}

void Frame::fill_window() {
    int mid_x = _width/2;
    int mid_y = _height/2;

    // first region w 0s
    for (int y = 0; y < mid_y; ++y) {
        for(int x = 0; x < mid_x; ++x) {
            mvwaddch(_w, y, x, '0');
        }
    }
    for (int y = 0; y < mid_y; ++y) {
        for(int x = mid_x; x < _width; ++x) {
            mvwaddch(_w, y, x, '1');
        }
    }
    for (int y = mid_y; y < _height; ++y) {
        for(int x = 0; x < mid_x; ++x) {
            mvwaddch(_w, y, x, '2');
        }
    }
    for (int y = mid_y; y < _height; ++y) {
        for(int x = mid_x; x < _width; ++x) {
            mvwaddch(_w, y, x, '3');
        }
    }

    for (int y = 0; y < _height; ++y) {
        mvwaddch(_w, y, 0, '|');
        mvwaddch(_w, y, _width - 1, '|');
    }

    for (int x = 0; x < _width; ++ x) {
        mvwaddch(_w, 0, x, '-');
        mvwaddch(_w, _height - 1, x, '-');
    }
}

void Frame::gen_Perlin(const unsigned int &seed) {
    PerlinNoise pn(seed);

    for (int i = 0; i < _height; ++i) {
        for (int j = 0; j < _width; ++j) {
            double x = (double)j/((double)_width);
            double y = (double)i/((double) _height);

            double n = pn.noise(10 * x, 10 * y, 0.8);

            // Water
            if (n < 0.35) {
                mvwaddch(_w, i, j, '~');
            } else if (n >= 0.35 && n < 0.6) {
                // Floors
                mvwaddch(_w, i, j, '.');
            } else if (n >= 0.6 && n < 0.8) {
                // Walls
                mvwaddch(_w, i, j, '#');
            } else {
                // Ice
                mvwaddch(_w, i, j, 'S');
            }
        }
    }
}

WINDOW* Frame::win() {
    return _w;
}

WINDOW* Frame::super() {
    return _super;
}

bool Frame::has_super() {
    return _has_super;
}

int Frame::height() {
    return _height;
}

int Frame::width() {
    return _width;
}

int Frame::row() {
    return _row;
}

int Frame::col() {
    return _col;
}

A  => src/frame.hpp +76 -0
@@ 1,76 @@
/*
 * =====================================================================================
 *
 *       Filename:  frame.hpp
 *
 *    Description:  Abstractions around NCurses windows 
 *
 *        Version:  1.0
 *        Created:  01/07/2023 09:55:52 PM
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  Cara Salter (muirrum), cara@devcara.com
 *   Organization:  
 *
 * =====================================================================================
 */

#pragma once

#include <ncurses.h>

#include "mob.hpp"

class Frame {
    // dimensions
    int _height, _width;
    // Position
    int _row, _col;
    // FALSE when root window and TRUE for a subwindow
    bool _has_super;
    // Pointer to an ncurses window
    WINDOW* _w;
    // The super-window, if exists
    WINDOW* _super;

    public:
        // Init with no parent
        Frame(int nr_rows, int nr_cols, int row_0, int col_0);
        // Init with parent window
        Frame(Frame &super, int nr_rows, int nr_cols, int row_0, int col_0);
        ~Frame();

        // Get window type
        bool has_super();

        WINDOW* win();
        WINDOW *super();

        // Get height of window
        int height();
        int width();
        int row();
        int col();

        void refresh();
        void move(int r, int c);

        // Fill a window with numbers, for debugging
        // Will look like this:
        //      0 | 1
        //      -----
        //      2 | 3
        void fill_window();

        void move_window(int r, int c);

        void erase(Mob &x);
        // Add a mob to the window
        void place_mob(Mob &x);
        void place_mob(Mob &x, int row_0, int col_0);
        // Center viewport around mob
        void center(Mob &x);

        void gen_Perlin(const unsigned int &seed);
};

A  => src/main.cpp +71 -0
@@ 1,71 @@
/*
 * =====================================================================================
 *
 *       Filename:  main.cpp
 *
 *    Description:  Entrypoint 
 *
 *        Version:  1.0
 *        Created:  01/07/2023 09:36:42 PM
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  Cara Salter (muirrum), cara@devcara.com
 *   Organization:  
 *
 * =====================================================================================
 */
#include <stdlib.h>
#include <ncurses.h>

#include "screen.hpp"
#include "mob.hpp"
#include "frame.hpp"

void game_loop(Frame &map, Frame &viewport, Mob &character, int ch);

int main() {
    Screen scr;
    
    scr.add("Welcome to the game!\nPress any key to start.");

    int ch = getch();
    
    Frame game_map(2*scr.height(), 2*scr.width(), 0, 0);

    Frame viewport(game_map, scr.height(), scr.width(), 0, 0);

    Mob character('@', game_map.height()/2, game_map.width()/2);

    game_map.gen_Perlin(237);

    game_loop(game_map, viewport, character, ch);

    return 0;
}

void game_loop(Frame &map, Frame &viewport, Mob &character, int ch) {
    if (ch == 'q' || ch == 'Q') return;

    map.place_mob(character);
    viewport.center(character);
    viewport.refresh();

    for(;;) {
        ch = getch();

        if (ch == 'h') {
            map.place_mob(character, character.row(), character.col() -1); 
        } else if (ch == 'j') {
            map.place_mob(character, character.row() + 1, character.col());
       } else if (ch == 'k') {
           map.place_mob(character, character.row() - 1, character.col());
        } else if (ch == 'l') {
            map.place_mob(character, character.row(), character.col() + 1);
        } else if (ch == 'q') {
            break;
        }
        viewport.center(character);
        viewport.refresh();
    }
}

A  => src/mob.cpp +43 -0
@@ 1,43 @@
/*
 * =====================================================================================
 *
 *       Filename:  mob.cpp
 *
 *    Description:  Implements a mobile entity 
 *
 *        Version:  1.0
 *        Created:  01/07/2023 10:02:44 PM
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  Cara Salter (muirrum), cara@devcara.com
 *   Organization:  
 *
 * =====================================================================================
 */
#include <stdlib.h>
#include <ncurses.h>
#include "mob.hpp"

Mob::Mob(char symbol, int row_0, int col_0) {
    _symbol = symbol;
    _row = row_0;
    _col = col_0;
}

void Mob::move(int row_0, int col_0) {
    _row = row_0;
    _col = col_0;
}

int Mob::row() {
    return _row;
}

int Mob::col() {
    return _col;
}

char Mob::symbol() {
    return _symbol;
}

A  => src/mob.hpp +34 -0
@@ 1,34 @@
/*
 * =====================================================================================
 *
 *       Filename:  mob.hpp
 *
 *    Description:  Defines a mobile entity 
 *
 *        Version:  1.0
 *        Created:  01/07/2023 09:54:14 PM
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  Cara Salter (muirrum), cara@devcara.com
 *   Organization:  
 *
 * =====================================================================================
 */

#pragma once
class Mob {
    int _row, _col;
    char _symbol;
    public:
        // Create a mob
        Mob(char symbol, int row_0, int col_0);
        // Change mob position
        void move(int row_0, int col_0);
        // Get character's row (y) position
        int row();
        // Get mob's col (x) position
        int col();
        // Get the symbol that represents this mob
        char symbol();
};

A  => src/noise.cpp +100 -0
@@ 1,100 @@
/*
 * =====================================================================================
 *
 *       Filename:  noise.cpp
 *
 *    Description:  Implementation of Perlin Noise 
 *
 *        Version:  1.0
 *        Created:  01/07/2023 10:55:49 PM
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  Cara Salter (muirrum), cara@devcara.com
 *   Organization:  
 *
 * =====================================================================================
 */
#include <stdlib.h>
#include <iostream>
#include <cmath>
#include <random>
#include <algorithm>
#include "noise.hpp"

PerlinNoise::PerlinNoise() {
    // Magic numbers... magic numbers everywhere
    p = {
        		151,160,137,91,90,15,131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,
		8,99,37,240,21,10,23,190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,
		35,11,32,57,177,33,88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,
		134,139,48,27,166,77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,
		55,46,245,40,244,102,143,54, 65,25,63,161,1,216,80,73,209,76,132,187,208, 89,
		18,169,200,196,135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,
		250,124,123,5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,
		189,28,42,223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 
		43,172,9,129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,
		97,228,251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,
		107,49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
		138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180 };

    // Duplicate
    p.insert(p.end(), p.begin(), p.end());
}

PerlinNoise::PerlinNoise(unsigned int seed) {
    p.resize(256);

    // fill p with values from 0 to 255
    std::iota(p.begin(), p.end(), 0);

    std::default_random_engine engine(seed);

    std::shuffle(p.begin(), p.end(), engine);

    p.insert(p.end(), p.begin(), p.end());
}

double PerlinNoise::noise(double x, double y, double z) {
    // Find the unit cube that contains the input
    int X = (int) floor(x) & 255;
    int Y = (int) floor(y) & 255;
    int Z = (int) floor(z) & 255;

    // find relative x, y, z in cube
    x -= floor(x);
    y -= floor(y);
    z -= floor(z);

    // Fade curves!
    double u = fade(x);
    double v = fade(y);
    double w = fade(z);

    // hash coordinates
	int A = p[X] + Y;
	int AA = p[A] + Z;
	int AB = p[A + 1] + Z;
	int B = p[X + 1] + Y;
	int BA = p[B] + Z;
	int BB = p[B + 1] + Z;

    // Blend!
    double res = lerp(w, lerp(v, lerp(u, grad(p[AA], x, y, z), grad(p[BA], x-1, y, z)), lerp(u, grad(p[AB], x, y-1, z), grad(p[BB], x-1, y-1, z))),	lerp(v, lerp(u, grad(p[AA+1], x, y, z-1), grad(p[BA+1], x-1, y, z-1)), lerp(u, grad(p[AB+1], x, y-1, z-1),	grad(p[BB+1], x-1, y-1, z-1))));
	return (res + 1.0)/2.0;
}

double PerlinNoise::fade(double t) {
    return t * t * t * (t * (t * 6 - 15) + 10);
}

double PerlinNoise::lerp(double t, double a, double b) {
    return a + t * (b - a);
}

double PerlinNoise::grad(int hash, double x, double y, double z) {
    int h = hash & 15;
	double u = h < 8 ? x : y,
		   v = h < 4 ? y : h == 12 || h == 14 ? x : z;
	return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v);
}

A  => src/noise.hpp +32 -0
@@ 1,32 @@
/*
 * =====================================================================================
 *
 *       Filename:  noise.hpp
 *
 *    Description:  Defining Perlin Noise 
 *
 *        Version:  1.0
 *        Created:  01/07/2023 10:52:51 PM
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  Cara Salter (muirrum), cara@devcara.com
 *   Organization:  
 *
 * =====================================================================================
 */
#include <vector>
#include <stdlib.h>

class PerlinNoise {
    // permutation vector
    std::vector<int> p;
    public:
        PerlinNoise();
        PerlinNoise(unsigned int seed);
        double noise(double x, double y, double z);
    private:
        double fade(double t);
        double lerp(double t, double a, double b);
        double grad(int hash, double x, double y, double z);
};

A  => src/screen.cpp +49 -0
@@ 1,49 @@
/*
 * =====================================================================================
 *
 *       Filename:  screen.cpp
 *
 *    Description:  Defines an NCurses screen 
 *
 *        Version:  1.0
 *        Created:  01/07/2023 09:52:27 PM
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  Cara Salter (muirrum), cara@devcara.com
 *   Organization:  
 *
 * =====================================================================================
 */

#include <ncurses.h>
#include <stdlib.h>
#include "screen.hpp"

Screen::Screen() {
    initscr();
    clear();
    noecho();
    cbreak();
    keypad(stdscr, TRUE);
    curs_set(0);

    getmaxyx(stdscr, _height, _width);
}

Screen::~Screen() {
    endwin();
}

// Print a message
void Screen::add(const char* message) {
    printw(message);
}

int Screen::height() {
    return _height;
}

int Screen::width() {
    return _width;
}

A  => src/screen.hpp +33 -0
@@ 1,33 @@
/*
 * =====================================================================================
 *
 *       Filename:  screen.hpp
 *
 *    Description:  NCurses screen 
 *
 *        Version:  1.0
 *        Created:  01/07/2023 09:52:54 PM
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  Cara Salter (muirrum), cara@devcara.com
 *   Organization:  
 *
 * =====================================================================================
 */


class Screen {
    int _height, _width;
    public:
        // Initialize NCurses
        Screen();
        // Clear NCurses
        ~Screen();
        // Print a message on the screen
        void add(const char* message);
        // Get the screen height
        int height();
        // Get the screen width
        int width();
};