commit 412eca38e2f2919ba0a11796065cf1c7ac9c0a08
Author: Christos Margiolis <christos@margiolis.net>
Date: Wed, 28 Apr 2021 03:06:33 +0300
initial commit
Diffstat:
39 files changed, 2190 insertions(+), 0 deletions(-)
diff --git a/LICENSE b/LICENSE
@@ -0,0 +1,20 @@
+MIT License
+
+(c) 2019-Present Christos Margiolis <christos@christosmarg.xyz>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the “Software”), to deal in
+the Software without restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
+Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/brainfuck/Makefile b/brainfuck/Makefile
@@ -0,0 +1,52 @@
+# brainfuck - a brainfuck interpreter
+.POSIX:
+
+include config.mk
+
+BIN = brainfuck
+DIST = ${BIN}-${VERSION}
+MAN1 = ${BIN}.1
+
+SRC = brainfuck.c
+OBJ = ${SRC:.c=.o}
+
+all: options ${BIN}
+
+options:
+ @echo ${BIN} build options:
+ @echo "CFLAGS = ${CFLAGS}"
+ @echo "LDFLAGS = ${LDFLAGS}"
+ @echo "CC = ${CC}"
+
+${BIN}: ${OBJ}
+ ${CC} ${LDFLAGS} ${OBJ} -o $@
+
+.c.o:
+ ${CC} -c ${CFLAGS} $<
+
+dist: clean
+ ${MKDIR} ${DIST}
+ ${CP} -R tests/ config.mk ${MAN1} ${SRC} LICENSE Makefile README.md ${DIST}
+ ${TAR} ${DIST}.tar ${DIST}
+ ${GZIP} ${DIST}.tar
+ ${RM_DIR} ${DIST}
+
+run:
+ ./${BIN}
+
+install: all
+ ${MKDIR} ${DESTDIR}${BIN_DIR} ${DESTDIR}${MAN_DIR}
+ ${CP} ${BIN} ${BIN_DIR}
+ ${CP} ${MAN1} ${DESTDIR}${MAN_DIR}
+ sed "s/VERSION/${VERSION}/g" < ${MAN1} > ${DESTDIR}${MAN_DIR}/${MAN1}
+ chmod 755 ${DESTDIR}${BIN_DIR}/${BIN}
+ chmod 644 ${DESTDIR}${MAN_DIR}/${MAN1}
+
+uninstall:
+ ${RM} ${DESTDIR}${BIN_DIR}/${BIN}
+ ${RM} ${DESTDIR}${MAN_DIR}/${MAN1}
+
+clean:
+ ${RM} ${BIN} ${OBJ} ${DIST}.tar.gz
+
+.PHONY: all options clean dist install uninstall run
diff --git a/brainfuck/README b/brainfuck/README
@@ -0,0 +1,16 @@
+brainfuck - a brainfuck interpreter
+==========================================
+brainfuck reads input from stdin and interprets it
+as Brainfuck source code.
+
+Usage
+-----
+ cd path/to/brainfuck
+ make
+ ./brainfuck < src.bf
+
+You can install brainfuck by running:
+
+ sudo make install clean
+
+The binary will be installed in usr/local/bin.
diff --git a/brainfuck/brainfuck.1 b/brainfuck/brainfuck.1
@@ -0,0 +1,25 @@
+.Dd brainfuck\-VERSION
+.Dt BRAINFUCK 1
+.Os
+.Sh NAME
+.Nm brainfuck
+.Nd a simple brainfuck interpreter
+.Sh SYNOPSIS
+.Nm
+stdin
+.Sh DESCRIPTION
+.Nm
+reads input from stdin and interprets it
+as Brainfuck source code.
+.Sh EXAMPLES
+To parse a Brainfuck source file
+.Pp
+.Dl $ brainfuck < src.bf
+.Pp
+Alternatively, you can pipe to
+.Nm
+.Pp
+.Dl $ cat src.bf | brainfuck
+.Pp
+.Sh AUTHORS
+.An Christos Margiolis Aq Mt christos@christosmarg.xyz
diff --git a/brainfuck/brainfuck.c b/brainfuck/brainfuck.c
@@ -0,0 +1,76 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define BUFSIZE 50000
+
+int
+main(int argc, char *argv[])
+{
+ size_t len = 0;
+ int closed, opened, pos = 0;
+ unsigned short *pc;
+ char buf[BUFSIZE], *src;
+
+ while (read(STDIN_FILENO, &buf[len], 1) > 0)
+ len++;
+ buf[len] = '\0';
+
+ if ((src = malloc(len)) == NULL) {
+ perror("malloc");
+ exit(EXIT_FAILURE);
+ }
+ strcpy(src, buf);
+ memset(buf, 0, len);
+
+ for (pc = (unsigned short *)buf; pos < len; pos++) {
+ switch (src[pos]) {
+ case '>':
+ pc++;
+ break;
+ case '<':
+ pc--;
+ break;
+ case '+':
+ (*pc)++;
+ break;
+ case '-':
+ (*pc)--;
+ break;
+ case '.':
+ putchar(*pc);
+ break;
+ case ',':
+ *pc = getchar();
+ break;
+ case '[':
+ if (!(*pc)) {
+ for (opened = 0, pos++; pos < len; pos++) {
+ if (src[pos] == ']' && !opened)
+ break;
+ else if (src[pos] == '[')
+ opened++;
+ else if (src[pos] == ']')
+ opened--;
+ }
+ }
+ break;
+ case ']':
+ if (*pc) {
+ for (closed = 0, pos--; pos >= 0; pos--) {
+ if (src[pos] == '[' && !closed)
+ break;
+ else if (src[pos] == ']')
+ closed++;
+ else if (src[pos] == '[')
+ closed--;
+ }
+ }
+ break;
+ }
+ }
+ free(src);
+
+ return 0;
+}
diff --git a/brainfuck/config.mk b/brainfuck/config.mk
@@ -0,0 +1,30 @@
+# brainfuck version
+VERSION = 0
+
+# paths
+PREFIX = /usr/local
+MAN_DIR = ${PREFIX}/share/man/man1
+BIN_DIR = ${PREFIX}/bin
+
+# includes and libs
+INCS = -Iinclude
+LIBS = -Llib
+
+# flags
+CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L \
+ -D_XOPEN_SOURCE=700 -DVERSION=\"${VERSION}\"
+CFLAGS = -std=c99 -pedantic -Wall -Os ${INCS} ${CPPFLAGS}
+LDFLAGS = ${LIBS}
+
+# utils
+CP = cp -f
+RM = rm -f
+RM_DIR = rm -rf
+MV = mv
+MKDIR = mkdir -p
+RM_DIR = rm -rf
+TAR = tar -cf
+GZIP = gzip
+
+# compiler
+CC = cc
diff --git a/brainfuck/tests/hello.bf b/brainfuck/tests/hello.bf
@@ -0,0 +1 @@
+++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.
diff --git a/brainfuck/tests/triangle.bf b/brainfuck/tests/triangle.bf
@@ -0,0 +1,5 @@
+++++++++[>+>++++<<-]>++>>+<[-[>>+<<-]+>>]>+[
+ -<<<[
+ ->[+[-]+>++>>>-<<]<[<]>>++++++[<<+++++>>-]+<<++.[-]<<
+ ]>.>+[>>]>+
+]
diff --git a/bytepusher/Makefile b/bytepusher/Makefile
@@ -0,0 +1,52 @@
+# bytepusher - a Bytepusher VM implementation
+.POSIX:
+
+include config.mk
+
+BIN = bytepusher
+DIST = ${BIN}-${VERSION}
+MAN1 = ${BIN}.1
+
+SRC = bytepusher.c
+OBJ = ${SRC:.c=.o}
+
+all: options ${BIN}
+
+options:
+ @echo ${BIN} build options:
+ @echo "CFLAGS = ${CFLAGS}"
+ @echo "LDFLAGS = ${LDFLAGS}"
+ @echo "CC = ${CC}"
+
+${BIN}: ${OBJ}
+ ${CC} ${LDFLAGS} ${OBJ} -o $@
+
+.c.o:
+ ${CC} -c ${CFLAGS} $<
+
+dist: clean
+ ${MKDIR} ${DIST}
+ ${CP} -R roms/ ${MAN1} ${SRC} config.mk LICENSE Makefile README.md ${DIST}
+ ${TAR} ${DIST}.tar ${DIST}
+ ${GZIP} ${DIST}.tar
+ ${RM_DIR} ${DIST}
+
+run:
+ ./${BIN}
+
+install: all
+ ${MKDIR} ${DESTDIR}${BIN_DIR} ${DESTDIR}${MAN_DIR}
+ ${CP} ${BIN} ${BIN_DIR}
+ ${CP} ${MAN1} ${DESTDIR}${MAN_DIR}
+ sed "s/VERSION/${VERSION}/g" < ${MAN1} > ${DESTDIR}${MAN_DIR}/${MAN1}
+ chmod 755 ${DESTDIR}${BIN_DIR}/${BIN}
+ chmod 644 ${DESTDIR}${MAN_DIR}/${MAN1}
+
+uninstall:
+ ${RM} ${DESTDIR}${BIN_DIR}/${BIN}
+ ${RM} ${DESTDIR}${MAN_DIR}/${MAN1}
+
+clean:
+ ${RM} ${BIN} ${OBJ} ${DIST}.tar.gz
+
+.PHONY: all options clean dist install uninstall run
diff --git a/bytepusher/README.md b/bytepusher/README.md
@@ -0,0 +1,32 @@
+# BytePusher
+
+A cross-platform BytePusher virtual machine implementation in C and SDL.
+You can learn about it here: https://esolangs.org/wiki/BytePusher
+
+## Usage
+
+SDL needs to be installed on your machine in order to run the program.
+The `roms` directory contains a few roms you can try out.
+```shell
+$ cd path/to/bytepusher
+$ make
+$ ./bytepusher [ROM]
+$ make clean # optional
+```
+You can install `bytepusher` by running `sudo make install clean`.
+The binary will be installed in `/usr/local/bin`.
+
+## Screenshots
+
+<table>
+ <tr>
+ <td><img align="left" src="https://user-images.githubusercontent.com/54286563/91668705-55886a00-eb17-11ea-94d2-07f53ca98971.png"/></td>
+ <td><img align="right" src="https://user-images.githubusercontent.com/54286563/91668704-54efd380-eb17-11ea-8c75-f1a35637499c.png"/></td>
+ <td><img align="left" src="https://user-images.githubusercontent.com/54286563/91668706-56210080-eb17-11ea-9448-812e3e664d57.png"/></td>
+ </tr>
+ <tr>
+ <td><img align="left" src="https://user-images.githubusercontent.com/54286563/91668708-56b99700-eb17-11ea-9c4e-7293c32d0485.png"/></td>
+ <td><img align="right" src="https://user-images.githubusercontent.com/54286563/91668712-56b99700-eb17-11ea-993e-74c2e160dbd3.png"/></td>
+ <td><img align="right" src="https://user-images.githubusercontent.com/54286563/91668707-56210080-eb17-11ea-8728-dc42b9443a0a.png"/></td>
+ </tr>
+</table>
diff --git a/bytepusher/bytepusher.1 b/bytepusher/bytepusher.1
@@ -0,0 +1,15 @@
+.Dd bytepusher\-VERSION
+.Dt BYTEPUSHER 1
+.Os
+.Sh NAME
+.Nm bytepusher
+.Nd a Bytepusher VM implementation
+.Sh SYNOPSIS
+.Nm
+.Op rom
+.Sh DESCRIPTION
+.Nm
+reads a Bytepusher source file and executes it. It
+uses the SDL2 library as a rendering API.
+.Sh AUTHORS
+.An Christos Margiolis Aq Mt christos@christosmarg.xyz
diff --git a/bytepusher/bytepusher.c b/bytepusher/bytepusher.c
@@ -0,0 +1,136 @@
+#include <stdlib.h>
+
+#include <SDL2/SDL.h>
+
+#ifdef _WIN_32
+typedef unsigned int u_int32_t;
+typedef unsigned short u_int16_t;
+typedef unsigned char u_int8_t;
+#else
+#include <sys/types.h>
+#endif /* _WIN_32 */
+
+#define RGB_CALC ((r) * 0x33 << 16 | (g) * 0x33 << 8 | (b) * 0x33)
+#define RGB_POS ((r) * 36 + (g) * 6 + (b))
+#define PC_POS (pc[3] << 16 | pc[4] << 8 | pc[5])
+
+static u_int32_t palette[0x100];
+static u_int8_t mem[0x1000008];
+static const u_int8_t keys[16] = {
+ SDLK_1, SDLK_2, SDLK_3, SDLK_4,
+ SDLK_q, SDLK_w, SDLK_e, SDLK_r,
+ SDLK_a, SDLK_s, SDLK_d, SDLK_f,
+ SDLK_z, SDLK_x, SDLK_c, SDLK_v
+};
+
+static int
+evhandle(void)
+{
+ SDL_Event ev;
+ size_t i;
+ u_int16_t keybits;
+
+ keybits = mem[0] << 8 | mem[1];
+ while (SDL_PollEvent(&ev)) {
+ if (ev.type == SDL_QUIT || ev.key.keysym.sym == SDLK_ESCAPE)
+ return 0;
+ if (ev.type == SDL_KEYDOWN)
+ for (i = 0; i < 16; i++)
+ if (ev.key.keysym.sym == keys[i])
+ keybits = (keybits & ~(1 << i)) |
+ (ev.type == SDL_KEYDOWN) << i;
+ }
+ mem[0] = keybits >> 8;
+ mem[1] = keybits & 0xFF;
+ return 1;
+}
+
+static void
+cycle(void)
+{
+ size_t i = 0x10000;
+ u_int8_t *pc;
+
+ pc = mem + (mem[2] << 16 | mem[3] << 8 | mem[4]);
+ while (i--) {
+ mem[PC_POS] = mem[pc[0] << 16 | pc[1] << 8 | pc[2]];
+ pc = mem + (pc[6] << 16 | pc[7] << 8 | pc[8]);
+ }
+}
+
+static void
+render(SDL_Renderer *ren, SDL_Texture *tex)
+{
+ u_int32_t pixels[0x10000];
+ u_int32_t *out;
+ u_int8_t *in;
+
+ in = mem + (mem[5] << 16);
+ out = pixels;
+ for (; out < (pixels + 0x10000); *out++ = palette[*in++])
+ ;
+ SDL_UpdateTexture(tex, 0, pixels,
+ 0x100 * SDL_BYTESPERPIXEL(SDL_PIXELFORMAT_BGRA32));
+ SDL_RenderClear(ren);
+ SDL_RenderCopy(ren, tex, 0, 0);
+ SDL_RenderPresent(ren);
+}
+
+int
+main(int argc, char *argv[])
+{
+ SDL_Window *win;
+ SDL_Renderer *ren;
+ SDL_Texture *tex;
+ FILE *fp;
+ size_t i;
+ int w = 256, h = 256;
+ u_int8_t r, g, b;
+
+ if (argc != 2) {
+ fprintf(stderr, "usage: %s rom\n", argv[0]);
+ return EXIT_FAILURE;
+ }
+ if ((fp = fopen(argv[1], "r")) == NULL) {
+ fprintf(stderr, "fopen: %s\n", argv[1]);
+ return EXIT_FAILURE;
+ }
+ for (i = 0; (mem[i] = fgetc(fp)) != EOF && i < sizeof(mem); i++)
+ ;
+ fclose(fp);
+
+ /* TODO: implement audio */
+ win = SDL_CreateWindow("bytepusher", SDL_WINDOWPOS_UNDEFINED,
+ SDL_WINDOWPOS_UNDEFINED, w, h, SDL_WINDOW_SHOWN);
+ ren = SDL_CreateRenderer(win, -1, 0);
+ tex = SDL_CreateTexture(ren, SDL_PIXELFORMAT_BGRA32,
+ SDL_TEXTUREACCESS_STATIC, w, h);
+
+ if (!win || !ren || !tex) {
+ fprintf(stderr, "SDL error: %s\n", SDL_GetError());
+ return EXIT_FAILURE;
+ }
+
+ for (r = 0; r < 6; r++)
+ for (g = 0; g < 6; g++)
+ for (b = 0; b < 6; b++)
+ palette[RGB_POS] = RGB_CALC;
+
+ for (i = 0xd8; i < 0x100; i++)
+ palette[i] = 0x000000;
+
+ for (;;) {
+ if (!evhandle())
+ break;
+ cycle();
+ render(ren, tex);
+ SDL_Delay(10);
+ }
+
+ SDL_DestroyTexture(tex);
+ SDL_DestroyRenderer(ren);
+ SDL_DestroyWindow(win);
+ SDL_Quit();
+
+ return EXIT_SUCCESS;
+}
diff --git a/bytepusher/config.mk b/bytepusher/config.mk
@@ -0,0 +1,30 @@
+# bytepusher version
+VERSION = 0
+
+# paths
+PREFIX = /usr/local
+MAN_DIR = ${PREFIX}/share/man/man1
+BIN_DIR = ${PREFIX}/bin
+
+# includes and libs
+INCS = -Iinclude
+LIBS = -Llib -lSDL2
+
+# flags
+CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L \
+ -D_XOPEN_SOURCE=700 -DVERSION=\"${VERSION}\"
+CFLAGS = -std=c99 -pedantic -Wall -Wno-deprecated-declarations -Os ${INCS} ${CPPFLAGS}
+LDFLAGS = ${LIBS}
+
+# utils
+CP = cp -f
+RM = rm -f
+RM_DIR = rm -rf
+MV = mv
+MKDIR = mkdir -p
+RM_DIR = rm -rf
+TAR = tar -cf
+GZIP = gzip
+
+# compiler
+CC = gcc
diff --git a/bytepusher/roms/invertloopsine.BytePusher b/bytepusher/roms/invertloopsine.BytePusher
Binary files differ.
diff --git a/bytepusher/roms/munchingsquares.BytePusher b/bytepusher/roms/munchingsquares.BytePusher
Binary files differ.
diff --git a/bytepusher/roms/nyan.BytePusher b/bytepusher/roms/nyan.BytePusher
Binary files differ.
diff --git a/bytepusher/roms/palettetest.BytePusher b/bytepusher/roms/palettetest.BytePusher
Binary files differ.
diff --git a/bytepusher/roms/scrollinglogo.BytePusher b/bytepusher/roms/scrollinglogo.BytePusher
Binary files differ.
diff --git a/bytepusher/roms/sinescroller.BytePusher b/bytepusher/roms/sinescroller.BytePusher
Binary files differ.
diff --git a/fnc/Makefile b/fnc/Makefile
@@ -0,0 +1,46 @@
+# See LICENSE file for copyright and license details.
+# fnc - a simple finance program
+.POSIX:
+
+PREFIX = /usr/local
+MAN_DIR = ${PREFIX}/share/man/man1
+BIN_DIR = ${PREFIX}/bin
+
+BIN = fnc
+VERSION = 0
+DIST = ${BIN}-${VERSION}
+MAN1 = ${BIN}.1
+
+SRC = fnc.go
+
+all: ${BIN}
+
+${BIN}: ${SRC}
+ go build ${SRC}
+
+dist: clean
+ mkdir -p ${DIST}
+ cp -fR fnc.1 fnc.go Makefile ${DIST}
+ tar -cf ${DIST}.tar ${DIST}
+ gzip ${DIST}.tar
+ rm -rf ${DIST}
+
+install: all
+ mkdir -p ${DESTDIR}${BIN_DIR} ${DESTDIR}${MAN_DIR}
+ cp -f ${BIN} ${BIN_DIR}
+ cp -f ${MAN1} ${DESTDIR}${MAN_DIR}
+ sed "s/VERSION/${VERSION}/g" < ${MAN1} > ${DESTDIR}${MAN_DIR}/${MAN1}
+ chmod 755 ${DESTDIR}${BIN_DIR}/${BIN}
+ chmod 644 ${DESTDIR}${MAN_DIR}/${MAN1}
+
+uninstall:
+ rm -f ${DESTDIR}${BIN_DIR}/${BIN}
+ rm -f ${DESTDIR}${MAN_DIR}/${MAN1}
+
+run:
+ ./${BIN}
+
+clean:
+ rm -f ${BIN} ${OBJ} ${DIST}.tar.gz
+
+.PHONY: all options clean dist install uninstall run
diff --git a/fnc/fnc.1 b/fnc/fnc.1
@@ -0,0 +1,28 @@
+.Dd fnc\-VERSION
+.Dt FNC 1
+.Os
+.Sh NAME
+.Nm fnc
+.Nd a simple finance program
+.Sh SYNOPSIS
+.Nm
+.Op Fl d Ar date
+.Ar file
+.Sh DESCRIPTION
+.Pp
+.Nm
+reads a CSV file structured like this:
+
+ Date,Item,Price
+ Feb2021,Foo,+1
+ Mar2021,Bar,-2
+ Apr2021,Baz,+3
+
+The dates must also not have spaces between them
+(e.g Feb 2021) in case you want to use the -d option.
+.Sh OPTIONS
+.Bl -tag -width -indent
+.It Fl d Ar date
+prints only the fields matching the specified date.
+.Sh AUTHORS
+.An Christos Margiolis Aq Mt christos@christosmarg.xyz
diff --git a/fnc/fnc.go b/fnc/fnc.go
@@ -0,0 +1,80 @@
+package main
+
+import (
+ "encoding/csv"
+ "fmt"
+ "io"
+ "log"
+ "os"
+ "strconv"
+)
+
+func usage() {
+ fmt.Fprintf(os.Stderr, "usage: %s [-d date] file\n", os.Args[0]);
+ os.Exit(1);
+}
+
+func main() {
+ var path, date string;
+ var n, loss, gain, total float64;
+ var arglen int;
+ var dflag bool;
+
+ arglen = len(os.Args);
+ if arglen - 1 == 0 {
+ usage();
+ }
+ path = os.Args[arglen - 1];
+ for i := 0; i < arglen; i++ {
+ if os.Args[i] == "-d" {
+ date = os.Args[i+1];
+ dflag = true;
+ } else if os.Args[i] == "-h" {
+ usage();
+ }
+ }
+
+ f, err := os.Open(path);
+ if err != nil {
+ log.Fatal(err);
+ }
+ defer f.Close();
+
+ csvr := csv.NewReader(f);
+ if err != nil {
+ log.Fatal(err);
+ }
+ header, err := csvr.Read();
+ if err != nil {
+ log.Fatal(err);
+ }
+ fmt.Println(header);
+
+ for {
+ line, err := csvr.Read();
+ if err != nil {
+ if err == io.EOF {
+ break;
+ }
+ log.Fatal(err);
+ }
+ if dflag && date != line[0] {
+ continue;
+ }
+ n, err = strconv.ParseFloat(line[2], 64);
+ if err != nil {
+ log.Fatal(err);
+ }
+ if n >= 0 {
+ gain += n;
+ } else {
+ loss += n;
+ }
+ total += n;
+ fmt.Printf("%-15s %-20s %-.2f\n", line[0], line[1], n);
+ }
+ fmt.Println();
+ fmt.Printf("Loss: %.2f\n", -loss);
+ fmt.Printf("Gain: %.2f\n", gain);
+ fmt.Printf("Total: %.2f\n", total);
+}
diff --git a/minecurses/Makefile b/minecurses/Makefile
@@ -0,0 +1,53 @@
+# minecurses - a terminal minesweeper game
+.POSIX:
+
+include config.mk
+
+BIN = minecurses
+DIST = ${BIN}-${VERSION}
+MAN6 = ${BIN}.6
+
+SRC = minecurses.c
+OBJ = ${SRC:.c=.o}
+
+all: options ${BIN}
+
+options:
+ @echo ${BIN} build options:
+ @echo "CFLAGS = ${CFLAGS}"
+ @echo "LDFLAGS = ${LDFLAGS}"
+ @echo "CC = ${CC}"
+
+${BIN}: ${OBJ}
+ ${CC} ${LDFLAGS} ${OBJ} -o $@
+
+.c.o:
+ ${CC} -c ${CFLAGS} $<
+
+dist: clean
+ ${MKDIR} ${DIST}
+ ${CP} -R log/ res/ config.mk defs.h LICENSE Makefile ${MAN} ${SRC} \
+ README.md ${DIST}
+ ${TAR} ${DIST}.tar ${DIST}
+ ${GZIP} ${DIST}.tar
+ ${RM_DIR} ${DIST}
+
+run:
+ ./${BIN}
+
+install: all
+ ${MKDIR} ${DESTDIR}${BIN_DIR} ${DESTDIR}${MAN_DIR}
+ ${CP} ${BIN} ${BIN_DIR}
+ ${CP} ${MAN6} ${DESTDIR}${MAN_DIR}
+ sed "s/VERSION/${VERSION}/g" < ${MAN6} > ${DESTDIR}${MAN_DIR}/${MAN6}
+ chmod 755 ${DESTDIR}${BIN_DIR}/${BIN}
+ chmod 644 ${DESTDIR}${MAN_DIR}/${MAN6}
+
+uninstall:
+ ${RM} ${DESTDIR}${BIN_DIR}/${BIN}
+ ${RM} ${DESTDIR}${MAN_DIR}/${MAN6}
+
+clean:
+ ${RM} ${BIN} ${OBJ} ${DIST}.tar.gz
+
+.PHONY: all options clean dist install uninstall run
diff --git a/minecurses/README.md b/minecurses/README.md
@@ -0,0 +1,47 @@
+# minecurses
+
+A minesweeper game using `ncurses`. The game was originally made as a
+university assignment of mine, but I decided to take it a bit further afterwards.
+
+## How to play
+
+The objective is to simply find and defuse all the mines, not open all the non-mine
+cells, like in most minesweeper games; this make the game relatively harder.
+
+* Enter number of columns
+* Enter number of rows
+* Enter number of mines
+* Move with `w`/`s`/`a`/`d` or Vim keys (`k`/`j`/`h`/`l`)
+* Open cell with `[ENTER]`, or `o`
+* Flag cell with `f`
+* Defuse mine (only if flagged) with `g`
+* You win if you defuse all the mines
+* You lose in case you open a mine or you try to defuse a flagged cell not containing a mine
+
+Additional controls
+
+* `m`: Open control menu
+* `r`: Restart the game
+* `q`: Quit
+
+## Usage
+
+```shell
+$ cd path/to/minecurses
+$ make && make run
+$ make clean # optional
+```
+You can install `minecurses` by running `sudo make install clean`.
+The binary will be installed in `/usr/local/bin`.
+
+## Preview
+
+### Start screen
+![startmenu](https://user-images.githubusercontent.com/54286563/102023826-b110f780-3d96-11eb-89a3-4b0679a36a50.png)
+### Gameplay
+![gameplay](https://user-images.githubusercontent.com/54286563/102023832-ba9a5f80-3d96-11eb-9341-b2a07a7356de.png)
+
+## To Do
+
+* Add colors
+* Fix occasional wrong mine spawning bug
diff --git a/minecurses/config.mk b/minecurses/config.mk
@@ -0,0 +1,30 @@
+# minecurses version
+VERSION = 0
+
+# paths
+PREFIX = /usr/local
+MAN_DIR = ${PREFIX}/share/man/man6
+BIN_DIR = ${PREFIX}/bin
+
+# includes and libs
+INCS = -Iinclude
+LIBS = -Llib -lncurses
+
+# flags
+CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L \
+ -D_XOPEN_SOURCE=700 -DVERSION=\"${VERSION}\"
+CFLAGS = -std=c99 -pedantic -Wall -Os ${INCS} ${CPPFLAGS}
+LDFLAGS = ${LIBS}
+
+# utils
+CP = cp -f
+RM = rm -f
+RM_DIR = rm -rf
+MV = mv
+MKDIR = mkdir -p
+RM_DIR = rm -rf
+TAR = tar -cf
+GZIP = gzip
+
+# compiler
+CC = gcc
diff --git a/minecurses/defs.h b/minecurses/defs.h
@@ -0,0 +1,68 @@
+#ifndef DEFS_H
+#define DEFS_H
+
+#define MOVE_UP_NORM 'k'
+#define MOVE_UP_VIM 'w'
+#define MOVE_DOWN_NORM 's'
+#define MOVE_DOWN_VIM 'j'
+#define MOVE_LEFT_NORM 'a'
+#define MOVE_LEFT_VIM 'h'
+#define MOVE_RIGHT_NORM 'd'
+#define MOVE_RIGHT_VIM 'l'
+#define MOVE_ENTER '\n'
+#define MOVE_OPEN_CELL 'o'
+#define MOVE_FLAG_CELL 'f'
+#define MOVE_DEFUSE_CELL 'g'
+#define MOVE_OPEN_MENU 'm'
+#define MOVE_RESTART 'r'
+#define MOVE_QUIT 'q'
+
+#define MINE_DEFUSED 'D'
+#define CELL_FLAGGED 'F'
+#define CELL_BLANK ' '
+#define CELL_MINE '*'
+#define GRID_BOX "[ ]"
+
+#define OPT_CTRLS "m Controls"
+#define OPT_QUIT "q Quit"
+#define OPT_RESTART "r Restart"
+#define OPT_MOVE_UP "w/k Move up"
+#define OPT_MOVE_DOWN "s/j Move down"
+#define OPT_MOVE_LEFT "a/h Move left"
+#define OPT_MOVE_RIGHT "d/l Move right"
+#define OPT_FLAG_CELL "f Flag cell"
+#define OPT_DEFUSE "g Defuse (if flagged only)"
+#define OPT_OPEN_CELL "[ENTER]/o Open cell"
+
+#define MSG_COLS "Columns (Min = %d, Max = %d): "
+#define MSG_ROWS "Rows (Min = %d, Max = %d): "
+#define MSG_MINES "Mines (Min = %d, Max = %d): "
+#define MSG_QUIT_MENU "Press any key to quit the menu"
+#define MSG_CONT "Press any key to continue"
+#define MSG_CURPOS "Current position: (%d, %d) "
+#define MSG_NDEFUSED "Defused mines: %d/%d"
+#define MSG_WIN_1 "You defused all the mines!"
+#define MSG_WIN_2 "You won :)"
+#define MSG_LOSE_1 "You hit a mine! (or tried to defuse the wrong cell)"
+#define MSG_LOSE_2 "Game over :("
+
+#define GAME_LOST 0
+#define GAME_WON 1
+
+#define YMAX(x) (getmaxy((x)))
+#define XMAX(x) (getmaxx((x)))
+#define YMID(x) (getmaxy((x)) >> 1)
+#define XMID(x) (getmaxx((x)) >> 1)
+#define SCRSPACE_X(x) ((x) * 3 + 2)
+#define SCRSPACE_Y(y) ((y) + 2)
+#define ARRSPACE_X(x) (((x) - 2) / 3)
+#define ARRSPACE_Y(y) ((y) - 1)
+#define CENTER(x, y) (((x) >> 1) - ((y) >> 1))
+#define CURS_UPDATE(m, y, x) (wmove((m)->gamewin, (y), (x)))
+#define IS_MINE(m, r, c) ((m)->mineboard[(r)][(c)] == CELL_MINE)
+#define IS_FLAGGED(m, r, c) ((m)->dispboard[(r)][(c)] == CELL_FLAGGED)
+#define IS_BLANK(m, r, c) ((m)->dispboard[(r)][(c)] == CELL_BLANK)
+#define OUT_OF_BOUNDS(m, r, c) ((r) < 0 || (r) > ((m)->rows - 1) || \
+ (c) < 0 || (c) > ((m)->cols - 1))
+
+#endif /* DEFS_H */
diff --git a/minecurses/minecurses.6 b/minecurses/minecurses.6
@@ -0,0 +1,39 @@
+.Dd minecurses\-VERSION
+.Dt MINECURSES 6
+.Os
+.Sh NAME
+.Nm minecurses
+.Nd a terminal minesweeper game
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+.Nm
+starts a minesweeper game using the
+.Xr ncurses 3
+library to draw the TUI.
+.Sh OPTIONS
+The following option are used during runtime in the TUI,
+not the command line.
+.Bl -tag -width 8n
+.It Sy q
+quit game
+.It Sy r
+restart game
+.It Sy w/k
+move up on the Y axis
+.It Sy s/j
+move down on the Y axis
+.It Sy a/h
+move left on the X axis
+.It Sy d/l
+move right on the X axis
+.It Sy f
+flag cell
+.It Sy g
+defuse mine (if flagged only)
+.It Sy ENTER/o
+open cell
+.Sh SEE ALSO
+.Xr ncurses 3
+.Sh AUTHORS
+.An Christos Margiolis Aq Mt christos@christosmarg.xyz
diff --git a/minecurses/minecurses.c b/minecurses/minecurses.c
@@ -0,0 +1,372 @@
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <ncurses.h>
+
+#include "defs.h"
+
+struct minecurses {
+ WINDOW *gamewin;
+ char **dispboard;
+ char **mineboard;
+ int rows;
+ int cols;
+ int nmines;
+ int ndefused;
+ int move;
+ int x;
+ int y;
+ int gameover;
+};
+
+static void gamereset(struct minecurses *);
+static void gamerestart(struct minecurses *);
+static void gamestart(struct minecurses *);
+static int valset(int, const char *, int, int);
+static int adjcount(const struct minecurses *, int, int);
+static void boardsdealloc(struct minecurses *);
+static void cellreveal(const struct minecurses *);
+static void boardprint(const struct minecurses *);
+static WINDOW *gamewininit(int, int);
+static void menuopts(void);
+static void endscreen(struct minecurses *, int);
+static void *emalloc(size_t);
+
+static void
+gamereset(struct minecurses *m)
+{
+ size_t i, j, r, c;
+
+ echo();
+ m->cols = valset(4, MSG_COLS, 5, ARRSPACE_X(XMAX(stdscr)) - 2);
+ m->rows = valset(3, MSG_ROWS, 5, ARRSPACE_Y(YMAX(stdscr)) - 3);
+ m->nmines = valset(2, MSG_MINES, 1, m->rows * m->cols - 15);
+ m->x = m->y = 0;
+ m->ndefused = 0;
+ m->gameover = 0;
+ noecho();
+
+ menuopts();
+ if (m->gamewin == NULL)
+ m->gamewin = gamewininit(m->rows, m->cols);
+
+ /* allocate memory for the boards */
+ m->dispboard = emalloc(m->rows * sizeof(char *));
+ m->mineboard = emalloc(m->rows * sizeof(char *));
+ for (i = 0; i < m->rows; i++) {
+ m->dispboard[i] = emalloc(m->cols);
+ m->mineboard[i] = emalloc(m->cols);
+ }
+
+ /* place mines */
+ srand(time(NULL));
+ for (i = 0; i < m->nmines; i++) {
+ r = rand() % m->rows;
+ c = rand() % m->cols;
+ m->mineboard[r][c] = CELL_MINE;
+ }
+
+ /* TODO: do it in one loop */
+ /* add numbers */
+ for (i = 0; i < m->rows; i++) {
+ for (j = 0; j < m->cols; j++) {
+ if (!IS_MINE(m, i, j))
+ m->mineboard[i][j] = adjcount(m, i, j) + '0';
+ /* in the meantime, initialize dispboard */
+ m->dispboard[i][j] = CELL_BLANK;
+ }
+ }
+
+ /* fill spaces */
+ for (i = 0; i < m->rows; i++)
+ for (j = 0; j < m->cols; j++)
+ if (!IS_MINE(m, i, j) && m->mineboard[i][j] == '0')
+ m->mineboard[i][j] = '-';
+}
+
+static void
+gamerestart(struct minecurses *m)
+{
+ boardsdealloc(m);
+ gamereset(m);
+}
+
+#define bx m->x
+#define by m->y
+
+static void
+gamestart(struct minecurses *m)
+{
+ static int y = 1, x = 2;
+
+ for (;;) {
+ erase();
+ // is this necessary?
+ delwin(m->gamewin);
+ m->gamewin = gamewininit(m->rows, m->cols);
+ refresh();
+ boardprint(m);
+
+ CURS_UPDATE(m, y, x);
+ bx = ARRSPACE_X(x);
+ by = ARRSPACE_Y(y);
+
+ /* session info */
+ mvprintw(0, 0, MSG_CURPOS, bx, by);
+ mvprintw(0, XMID(stdscr) - ((strlen(MSG_NDEFUSED) - 2) >> 1),
+ MSG_NDEFUSED, m->ndefused, m->nmines);
+ mvprintw(0, XMAX(stdscr) - strlen(OPT_CTRLS), OPT_CTRLS);
+
+ refresh();
+
+ /* handle moves */
+ switch (m->move = wgetch(m->gamewin)) {
+ case MOVE_UP_NORM: /* FALLTHROUGH */
+ case MOVE_UP_VIM:
+ if (--y < 1)
+ y = 1;
+ break;
+ case MOVE_DOWN_NORM: /* FALLTHROUGH */
+ case MOVE_DOWN_VIM:
+ if (++y > XMAX(m->gamewin) - 2)
+ y = YMAX(m->gamewin) - 2;
+ break;
+ case MOVE_LEFT_NORM: /* FALLTHROUGH */
+ case MOVE_LEFT_VIM:
+ x -= 3;
+ if (x < 2)
+ x = 2;
+ break;
+ case MOVE_RIGHT_NORM: /* FALLTHROUGH */
+ case MOVE_RIGHT_VIM:
+ x += 3;
+ if (x > XMAX(m->gamewin) - 3)
+ x = XMAX(m->gamewin) - 3;
+ break;
+ case MOVE_ENTER: /* FALLTHROUGH */
+ case MOVE_OPEN_CELL:
+ m->dispboard[by][bx] = m->mineboard[by][bx];
+ m->gameover = IS_MINE(m, by, bx);
+ cellreveal(m);
+ break;
+ case MOVE_FLAG_CELL:
+ if (IS_FLAGGED(m, by, bx))
+ m->dispboard[by][bx] = CELL_BLANK;
+ else if (!IS_FLAGGED(m, by, bx) && !IS_BLANK(m, by, bx))
+ break;
+ else
+ m->dispboard[by][bx] = CELL_FLAGGED;
+ cellreveal(m);
+ break;
+ case MOVE_DEFUSE_CELL:
+ if (IS_FLAGGED(m, by, bx) && IS_MINE(m, by, bx)) {
+ m->ndefused++;
+ m->dispboard[by][bx] = MINE_DEFUSED;
+ m->mineboard[by][bx] = MINE_DEFUSED;
+ cellreveal(m);
+ } else if (IS_FLAGGED(m, by, bx) && !IS_MINE(m, by, bx))
+ m->gameover = 1;
+ break;
+ case MOVE_OPEN_MENU:
+ menuopts();
+ box(m->gamewin, 0, 0);
+ boardprint(m);
+ break;
+ case MOVE_RESTART:
+ gamerestart(m);
+ break;
+ }
+
+ if (OUT_OF_BOUNDS(m, by, bx)
+ || m->ndefused > m->nmines
+ || m->gameover
+ || m->move == MOVE_QUIT)
+ break;
+ }
+
+ if (m->gameover)
+ endscreen(m, GAME_LOST);
+ else if (m->ndefused == m->nmines)
+ endscreen(m, GAME_WON);
+}
+
+#undef bx
+#undef by
+
+static int
+valset(int offy, const char *msg, int min, int max)
+{
+ int val;
+
+ do {
+ mvprintw(YMAX(stdscr)-offy, 1, msg, min, max);
+ scanw("%d", &val);
+ } while (val < min || val > max);
+ return val;
+}
+
+static int
+adjcount(const struct minecurses *m, int r, int c)
+{
+ int n = 0;
+
+ if (!OUT_OF_BOUNDS(m, r, c-1) && IS_MINE(m, r, c-1)) n++; // north
+ if (!OUT_OF_BOUNDS(m, r, c+1) && IS_MINE(m, r, c+1)) n++; // south
+ if (!OUT_OF_BOUNDS(m, r+1, c) && IS_MINE(m, r+1, c)) n++; // east
+ if (!OUT_OF_BOUNDS(m, r-1, c) && IS_MINE(m, r-1, c)) n++; // west
+ if (!OUT_OF_BOUNDS(m, r+1, c-1) && IS_MINE(m, r+1, c-1)) n++; // north-east
+ if (!OUT_OF_BOUNDS(m, r-1, c-1) && IS_MINE(m, r-1, c-1)) n++; // north-west
+ if (!OUT_OF_BOUNDS(m, r+1, c+1) && IS_MINE(m, r+1, c+1)) n++; // south-east
+ if (!OUT_OF_BOUNDS(m, r-1, c+1) && IS_MINE(m, r-1, c+1)) n++; // south-west
+
+ return n;
+}
+
+static void
+boardsdealloc(struct minecurses *m)
+{
+ size_t i = 0;
+
+ if (!m->dispboard && !m->mineboard) {
+ for (; i < m->rows; i++) {
+ free(m->dispboard[i]);
+ free(m->mineboard[i]);
+ }
+ free(m->dispboard);
+ free(m->mineboard);
+ }
+}
+
+static void
+cellreveal(const struct minecurses *m)
+{
+ int y = m->y + 1;
+ int x = SCRSPACE_X(m->x);
+
+ mvwaddch(m->gamewin, y, x, m->dispboard[m->y][m->x]);
+}
+
+static void
+boardprint(const struct minecurses *m)
+{
+ size_t i, j, x, y;
+
+ wattroff(m->gamewin, A_BOLD);
+ for (i = 1; i <= m->rows; i++) {
+ wmove(m->gamewin, i, 1);
+ for (j = 0; j < m->cols; j++)
+ wprintw(m->gamewin, GRID_BOX);
+ }
+
+ wattron(m->gamewin, A_BOLD);
+ for (i = 0, y = 1; i < m->rows; i++, y++)
+ for (j = 0, x = 2; j < m->cols; j++, x += 3)
+ mvwaddch(m->gamewin, y, x, m->dispboard[i][j]);
+}
+
+static WINDOW *
+gamewininit(int rows, int cols)
+{
+ WINDOW *gw;
+ int wr, wc, wy, wx;
+
+ wr = SCRSPACE_Y(rows);
+ wc = SCRSPACE_X(cols);
+ wy = CENTER(YMAX(stdscr), wr);
+ wx = CENTER(XMAX(stdscr), wc);
+ gw = newwin(wr, wc, wy, wx);
+ wattron(gw, A_BOLD);
+ box(gw, 0, 0);
+ wattroff(gw, A_BOLD);
+
+ return gw;
+}
+
+static void
+menuopts(void)
+{
+ WINDOW *opts;
+ int w, h, wy, wx;
+
+ w = 36;
+ h = 13;
+ wy = CENTER(YMAX(stdscr), h);
+ wx = CENTER(XMAX(stdscr), w);
+ opts = newwin(h, w, wy, wx);
+ box(opts, 0, 0);
+
+ /* fill menu */
+ mvwprintw(opts, 1, 1, OPT_QUIT);
+ mvwprintw(opts, 2, 1, OPT_RESTART);
+ mvwprintw(opts, 3, 1, OPT_MOVE_UP);
+ mvwprintw(opts, 4, 1, OPT_MOVE_DOWN);
+ mvwprintw(opts, 5, 1, OPT_MOVE_LEFT);
+ mvwprintw(opts, 6, 1, OPT_MOVE_RIGHT);
+ mvwprintw(opts, 7, 1, OPT_FLAG_CELL);
+ mvwprintw(opts, 8, 1, OPT_DEFUSE);
+ mvwprintw(opts, 9, 1, OPT_OPEN_CELL);
+ mvwprintw(opts, 11, 1, MSG_QUIT_MENU);
+
+ wgetch(opts);
+ delwin(opts);
+}
+
+static void
+endscreen(struct minecurses *m, int state)
+{
+ curs_set(0);
+ wclear(m->gamewin);
+ wrefresh(m->gamewin);
+ attron(A_BOLD);
+ switch (state) {
+ case GAME_WON:
+ mvprintw(YMID(stdscr)-2, XMID(stdscr)-11, MSG_WIN_1);
+ mvprintw(YMID(stdscr)-1, XMID(stdscr)-3, MSG_WIN_2);
+ break;
+ case GAME_LOST:
+ mvprintw(YMID(stdscr)-2, XMID(stdscr)-24, MSG_LOSE_1);
+ mvprintw(YMID(stdscr)-1, XMID(stdscr)-4, MSG_LOSE_2);
+ // TODO: print mine board
+ break;
+ }
+ mvprintw(YMID(stdscr), XMID(stdscr)-11, MSG_CONT);
+ refresh();
+ attroff(A_BOLD);
+ getchar();
+ // TODO: restart option
+}
+
+static void *
+emalloc(size_t nb)
+{
+ void *p;
+
+ if ((p = malloc(nb)) == NULL) {
+ fputs("malloc", stderr);
+ exit(EXIT_FAILURE);
+ }
+ return p;
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct minecurses m;
+
+ if (!initscr()) {
+ fputs("initscr", stderr);
+ return 1;
+ }
+ noecho();
+ cbreak();
+
+ gamereset(&m);
+ gamestart(&m);
+
+ boardsdealloc(&m);
+ delwin(m.gamewin);
+ endwin();
+
+ return EXIT_SUCCESS;
+}
diff --git a/morse/dash.wav b/morse/dash.wav
Binary files differ.
diff --git a/morse/dot.wav b/morse/dot.wav
Binary files differ.
diff --git a/morse/morse.cpp b/morse/morse.cpp
@@ -0,0 +1,96 @@
+#include <iostream>
+#include <map>
+#include <string>
+#include <SDL2/SDL.h>
+#include <SDL2/SDL_mixer.h>
+
+static void
+play(Mix_Chunk *sound)
+{
+ Mix_PlayChannel(-1, sound, 0);
+ while (Mix_Playing(-1) != 0)
+ SDL_Delay(200);
+}
+
+int
+main(int argc, char *argv[])
+{
+ Mix_Chunk *dot = nullptr;
+ Mix_Chunk *dash = nullptr;
+ std::string in, out;
+ std::map<char, std::string>::const_iterator it;
+ std::map<char, std::string> morsetable = {
+ {'a', ".-" }, {'A', ".-" },
+ {'b', "-..." }, {'B', "-..." },
+ {'c', "-.-." }, {'C', "-.-." },
+ {'d', "-.." }, {'D', "-.." },
+ {'e', "." }, {'E', "." },
+ {'f', "..-." }, {'F', "..-." },
+ {'g', "--." }, {'G', "--." },
+ {'h', "...." }, {'H', "...." },
+ {'i', ".." }, {'I', ".." },
+ {'j', ".---" }, {'J', ".---" },
+ {'k', "-.-" }, {'K', "-.-" },
+ {'l', ".-.." }, {'L', ".-.." },
+ {'m', "--" }, {'M', "--" },
+ {'n', "-." }, {'N', "-." },
+ {'o', "---" }, {'O', "---" },
+ {'p', ".--." }, {'P', ".--." },
+ {'q', "--.-" }, {'Q', "--.-" },
+ {'r', ".-." }, {'R', ".-." },
+ {'s', "..." }, {'S', "..." },
+ {'t', "-" }, {'T', "-" },
+ {'u', "..-" }, {'U', "..-" },
+ {'v', "...-" }, {'V', "...-" },
+ {'w', ".--" }, {'W', ".--" },
+ {'x', "-..-" }, {'X', "-..-" },
+ {'y', "-.--" }, {'Y', "-.--" },
+ {'z', "--.." }, {'Z', "--.." },
+ {'&', ".-..." }, {'\'', ".----."},
+ {'@', ".--.-." }, {':', "---..." },
+ {'(', "-.--." }, {')', "-.--.-" },
+ {',', "--..--" }, {'=', "-...-" },
+ {'!', "-.-.--" }, {'.', ".-.-.-" },
+ {'-', "-....-" }, {'+', ".-.-." },
+ {'\"', ".-..-."}, {'\?', "..--.."},
+ {'/', "-..-." }, {' ', " / " }
+ };
+
+ if (SDL_Init(SDL_INIT_AUDIO) == -1)
+ return -1;
+ Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 1, 4096);
+ dot = Mix_LoadWAV("dot.wav");
+ dash = Mix_LoadWAV("dash.wav");
+
+ std::cout << "Press CTRL+C to exit" << std::endl;
+ for (;;) {
+ std::cout << "> ";
+ std::getline(std::cin, in);
+ for (const char& c : in) {
+ it = morsetable.find(c);
+ if (morsetable.find(c) != morsetable.end())
+ out += it->second;
+ else
+ std::cerr << "Character '" << c <<
+ "' not found." << std::endl;
+ out += " ";
+ }
+ if (!out.empty()) {
+ std::cout << out << std::endl;
+ for (const char& c : out) {
+ if (c == '.')
+ play(dot);
+ else if (c == '-')
+ play(dash);
+ }
+ out.clear();
+ }
+ }
+
+ Mix_FreeChunk(dot);
+ Mix_FreeChunk(dash);
+ Mix_CloseAudio();
+ SDL_Quit();
+
+ return 0;
+}
diff --git a/other/bounce.c b/other/bounce.c
@@ -0,0 +1,55 @@
+#include <ncurses.h>
+#include <stdio.h>
+#include <unistd.h>
+
+struct ball {
+ int x;
+ int y;
+ int vx;
+ int vy;
+ int xmax;
+ int ymax;
+};
+
+static void
+init_curses(void)
+{
+ initscr();
+ cbreak();
+ noecho();
+ curs_set(0);
+}
+
+static void
+collision(struct ball *b)
+{
+ if (b->y < 2 || b->y > b->ymax-1)
+ b->vy *= -1.0f;
+ if (b->x < 1 || b->x > b->xmax-1)
+ b->vx *= -1.0f;
+ b->y += b->vy;
+ b->x += b->vx;
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct ball b = {1, 2, 1, 1, 0, 0};
+
+ init_curses();
+ getmaxyx(stdscr, b.ymax, b.xmax);
+
+ for (;;)
+ {
+ erase();
+ collision(&b);
+ mvaddch(b.y, b.x, 'O');
+ mvprintw(0, 0, "(x, y) = (%d, %d)", b.x, b.y);
+ mvhline(1, 0, ACS_HLINE, b.xmax);
+ refresh();
+ usleep(15000);
+ }
+ endwin();
+
+ return 0;
+}
diff --git a/other/mandelbrot.c b/other/mandelbrot.c
@@ -0,0 +1,59 @@
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#define IMGW 1920
+#define IMGH 1080
+#define MAXN 1024
+#define MINR (-1.5)
+#define MAXR 0.7
+#define MINI (-1.0)
+#define MAXI 1.0
+
+#define TOREAL(x, imgw, minr, maxr) (x * ((maxr - minr) / imgw) + minr)
+#define TOIMGN(y, imgh, mini, maxi) (y * ((maxi - mini) / imgh) + mini)
+
+static int
+fmandelbrot(double cr, double ci)
+{
+ int i = 0;
+ double zr = 0.0, zi = 0.0;
+ double tmp;
+
+ for ( ; i < MAXN && (zr * zr + zi * zi) < 4.0; i++) {
+ tmp = zr * zr - zi * zi + cr;
+ zi = 2.0 * zr * zi + ci;
+ zr = tmp;
+ }
+ return i;
+}
+
+int
+main(int argc, char *argv[])
+{
+ FILE *fp;
+ int x, y, n, r, g, b;
+ double cr, ci;
+
+ if ((fp = fopen("mandelbrot_output.ppm", "w")) == NULL) {
+ fprintf(stderr, "Cannot open output file. Exiting...");
+ return EXIT_FAILURE;
+ }
+ fprintf(fp, "P3\n%d %d\n256\n", IMGW, IMGH);
+
+ for (y = 0; y < IMGH; y++) {
+ for (x = 0; x < IMGW; x++) {
+ cr = TOREAL(x, IMGW, MINR, MAXR);
+ ci = TOIMGN(y, IMGH, MINI, MAXI);
+ n = fmandelbrot(cr, ci);
+ r = (n % 256);
+ g = ((int)(n * sinf(M_PI)) % 256);
+ b = ((n * 3) % 256);
+ fprintf(fp, "%d %d %d ", r, g, b);
+ }
+ fprintf(fp, "\n");
+ }
+ fclose(fp);
+
+ return 0;
+}
diff --git a/other/nnc.cpp b/other/nnc.cpp
@@ -0,0 +1,167 @@
+#include <algorithm>
+#include <cmath>
+#include <cstdlib>
+#include <ctime>
+#include <vector>
+#include <ncurses.h>
+
+static constexpr int ymax()
+{
+ return getmaxy(stdscr);
+}
+
+static constexpr int xmax()
+{
+ return getmaxx(stdscr);
+}
+
+struct Color {
+ int x, y;
+ int color;
+
+ Color(int x, int y, int color)
+ :x(x), y(y), color(color) {}
+
+ constexpr int dist(const Color& c)
+ {
+ int dx = this->x - c.x;
+ int dy = this->y - c.y;
+
+ return std::sqrt(dx * dx + dy * dy);
+ }
+};
+
+struct Blue: public Color {
+ Blue(int x, int y, int color)
+ :Color(x, y, color) {}
+};
+
+struct Green: public Color {
+ Green(int x, int y, int color)
+ :Color(x, y, color) {}
+};
+
+struct White: public Color {
+ White(int x, int y, int color)
+ :Color(x, y, color) {}
+};
+
+static void
+init_curses()
+{
+ initscr();
+ cbreak();
+ noecho();
+ curs_set(0);
+ start_color();
+ init_pair(1, COLOR_BLUE, COLOR_BLUE);
+ init_pair(2, COLOR_GREEN, COLOR_GREEN);
+ init_pair(3, COLOR_WHITE, COLOR_WHITE);
+}
+
+static void
+makepts(std::vector<Color *>& points)
+{
+ int x, y;
+
+ for (std::size_t i = 0; i < 5; i++) {
+ x = std::rand() % (xmax() - 1);
+ y = std::rand() % (ymax() - 1);
+ points.push_back(new Blue(x, y, 1));
+ }
+ for (std::size_t i = 0; i < 5; i++) {
+ x = std::rand() % (xmax() - 1);
+ y = std::rand() % (ymax() - 1);
+ points.push_back(new Green(x, y, 2));
+ }
+}
+
+static void
+makewts(std::vector<White *>& whites)
+{
+ int x, y;
+
+ for (std::size_t i = 0; i < 5; i++) {
+ x = std::rand() % (xmax() - 1);
+ y = std::rand() % (ymax() - 1);
+ whites.push_back(new White(x, y, 3));
+ }
+}
+
+template<typename T> static void
+print(const std::vector<T *>& vec)
+{
+ for (auto& v : vec) {
+ attron(COLOR_PAIR(v->color));
+ mvaddch(v->y, v->x, ACS_CKBOARD);
+ attroff(COLOR_PAIR(v->color));
+ }
+}
+
+static std::vector<int>
+calc_dists(const std::vector<Color *>& points, const White& w)
+{
+ std::vector<int> dists;
+
+ for (auto& point : points)
+ dists.push_back(point->dist(w));
+ return dists;
+}
+
+static void
+find_nn(const std::vector<Color *>& points, std::vector<White *>& whites)
+{
+ std::vector<int> dists;
+ int mindist;
+
+ for (const auto& point : points) {
+ for (auto&& white : whites) {
+ dists = calc_dists(points, *white);
+ mindist = *std::min_element(dists.begin(), dists.end());
+ if (point->dist(*white) == mindist)
+ white->color = point->color;
+ }
+ }
+}
+
+template<typename T> static void
+dealloc(std::vector<T *>& vec)
+{
+ for (auto&& v : vec)
+ if (v != nullptr)
+ delete v;
+ if (!vec.empty())
+ vec.clear();
+}
+
+int
+main(int argc, char **argv)
+{
+ init_curses();
+ std::srand(std::time(nullptr));
+
+ std::vector<Color *> points;
+ std::vector<White *> whites;
+ makepts(points);
+ makewts(whites);
+
+ erase();
+ print<Color>(points);
+ print<White>(whites);
+ refresh();
+ getch();
+
+ find_nn(points, whites);
+
+ erase();
+ print<Color>(points);
+ print<White>(whites);
+ refresh();
+ getch();
+
+ endwin();
+ dealloc<Color>(points);
+ dealloc<White>(whites);
+
+ return 0;
+}
diff --git a/other/snake.cpp b/other/snake.cpp
@@ -0,0 +1,184 @@
+#include <chrono>
+#include <cstdlib>
+#include <ctime>
+#include <list>
+#include <thread>
+
+#include <ncurses.h>
+
+#define XMAX (getmaxx(stdscr))
+#define YMAX (getmaxy(stdscr))
+
+struct Snake {
+ struct Seg {
+ int x;
+ int y;
+ };
+ std::list<Seg> body;
+ int x;
+ int y;
+ int score;
+ bool dead;
+
+ Snake();
+ void update(int key);
+ void grow();
+ void draw();
+ bool collided();
+};
+
+struct Food {
+ int x;
+ int y;
+
+ Food();
+ void spawn();
+ void draw();
+};
+
+Snake::Snake()
+{
+ y = YMAX >> 1;
+ x = XMAX >> 1;
+ body = {{x, y}, {x + 1, y + 1}};
+ score = 1;
+ dead = false;
+}
+
+void
+Snake::update(int key)
+{
+ switch (key) {
+ case KEY_UP:
+ y--;
+ body.push_front({body.front().x, body.front().y - 1});
+ break;
+ case KEY_DOWN:
+ y++;
+ body.push_front({body.front().x, body.front().y + 1});
+ break;
+ case KEY_LEFT:
+ x--;
+ body.push_front({body.front().x - 1, body.front().y});
+ break;
+ case KEY_RIGHT:
+ x++;
+ body.push_front({body.front().x + 1, body.front().y});
+ break;
+ }
+ body.pop_back();
+}
+
+void
+Snake::grow()
+{
+ for (int i = 0; i < 3; i++)
+ body.push_back({body.back().x, body.back().y});
+}
+
+void
+Snake::draw()
+{
+ for (const auto& b : body)
+ mvaddch(b.y, b.x, ACS_CKBOARD);
+}
+
+bool
+Snake::collided()
+{
+ dead = y < 2 || y > YMAX - 1 || x < 1 || x > XMAX - 1;
+ for (std::list<Seg>::iterator i = body.begin(); i != body.end(); i++)
+ if (i != body.begin()
+ && i->x == body.front().x
+ && i->y == body.front().y)
+ dead = true;
+ return dead;
+}
+
+Food::Food()
+{
+ x = std::rand() % XMAX - 1;
+ y = std::rand() % (YMAX - 2) + 2;
+}
+
+void
+Food::spawn()
+{
+ x = std::rand() % XMAX - 1;
+ y = std::rand() % (YMAX - 2) + 2;
+}
+
+void
+Food::draw()
+{
+ mvaddch(y, x, 'O');
+}
+
+static void
+initcurses()
+{
+ initscr();
+ cbreak();
+ noecho();
+ curs_set(0);
+ keypad(stdscr, true);
+ nodelay(stdscr, true);
+}
+
+static bool
+kbhit()
+{
+ int c;
+
+ if ((c = getch()) != ERR) {
+ ungetch(c);
+ return true;
+ }
+ return false;
+}
+
+int
+main(int argc, char *argv[])
+{
+ initcurses();
+ std::srand(std::time(nullptr));
+
+ Snake snake;
+ Food food;
+ int key, nextkey;
+
+ key = KEY_RIGHT;
+
+ for (;;) {
+ erase();
+ if (kbhit()) {
+ if ((nextkey = getch()) != key)
+ key = nextkey;
+ else
+ continue;
+ }
+
+ snake.update(key);
+ if (snake.collided() || key == 'q')
+ break;
+
+ if (snake.body.front().y == food.y
+ && snake.body.front().x == food.x) {
+ food.spawn();
+ snake.grow();
+ snake.score++;
+ }
+
+ food.draw();
+ snake.draw();
+
+ mvprintw(0, 0, "Score: %d", snake.score);
+ mvhline(1, 0, ACS_HLINE, XMAX);
+ refresh();
+ std::this_thread::sleep_for(std::chrono::milliseconds(60));
+ }
+
+ endwin();
+
+ return 0;
+}
diff --git a/shitcoin/Makefile b/shitcoin/Makefile
@@ -0,0 +1,52 @@
+# shitcoin - a cryptocurrency made as an experiment
+.POSIX:
+
+include config.mk
+
+BIN = shitcoin
+DIST = ${BIN}-${VERSION}
+MAN1 = ${BIN}.1
+
+SRC = shitcoin.c
+OBJ = ${SRC:.c=.o}
+
+all: options ${BIN}
+
+options:
+ @echo ${BIN} build options:
+ @echo "CFLAGS = ${CFLAGS}"
+ @echo "LDFLAGS = ${LDFLAGS}"
+ @echo "CC = ${CC}"
+
+${BIN}: ${OBJ}
+ ${CC} ${LDFLAGS} ${OBJ} -o $@
+
+.c.o:
+ ${CC} -c ${CFLAGS} $<
+
+dist: clean
+ ${MKDIR} ${DIST}
+ ${CP} -R config.mk shitcoin.c Makefile LICENSE README ${DIST}
+ ${TAR} ${DIST}.tar ${DIST}
+ ${GZIP} ${DIST}.tar
+ ${RM_DIR} ${DIST}
+
+run:
+ ./${BIN}
+
+install: all
+ ${MKDIR} ${DESTDIR}${BIN_DIR} ${DESTDIR}${MAN_DIR}
+ ${CP} ${BIN} ${BIN_DIR}
+ ${CP} ${MAN1} ${DESTDIR}${MAN_DIR}
+ sed "s/VERSION/${VERSION}/g" < ${MAN1} > ${DESTDIR}${MAN_DIR}/${MAN1}
+ chmod 755 ${DESTDIR}${BIN_DIR}/${BIN}
+ chmod 644 ${DESTDIR}${MAN_DIR}/${MAN1}
+
+uninstall:
+ ${RM} ${DESTDIR}${BIN_DIR}/${BIN}
+ ${RM} ${DESTDIR}${MAN_DIR}/${MAN1}
+
+clean:
+ ${RM} ${BIN} ${OBJ} ${DIST}.tar.gz
+
+.PHONY: all options clean dist install uninstall run
diff --git a/shitcoin/README b/shitcoin/README
@@ -0,0 +1,18 @@
+shitcoin - a useless cryptocurrency
+====================================
+don't use it, it's just for fun
+
+Installation
+-------------
+Edit config.mk first
+
+ $ make
+ # make install clean
+
+shitcoin will be installed in /usr/local/bin by default.
+
+To-Do
+-----
+- sign transactions
+- generate key pairs
+- implement simple wallet
diff --git a/shitcoin/config.mk b/shitcoin/config.mk
@@ -0,0 +1,30 @@
+# shitcoin version
+VERSION = 0
+
+# paths
+PREFIX = /usr/local
+MAN_DIR = ${PREFIX}/share/man/man1
+BIN_DIR = ${PREFIX}/bin
+
+# includes and libs
+INCS = -Iinclude
+LIBS = -Llib -lmd
+
+# flags
+CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L \
+ -D_XOPEN_SOURCE=700 -DVERSION=\"${VERSION}\"
+CFLAGS = -std=c99 -pedantic -Wall -Os ${INCS} ${CPPFLAGS}
+LDFLAGS = ${LIBS}
+
+# utils
+CP = cp -f
+RM = rm -f
+RM_DIR = rm -rf
+MV = mv
+MKDIR = mkdir -p
+RM_DIR = rm -rf
+TAR = tar -cf
+GZIP = gzip
+
+# compiler
+CC = cc
diff --git a/shitcoin/shitcoin.c b/shitcoin/shitcoin.c
@@ -0,0 +1,276 @@
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <sha256.h>
+
+#define HASH_LEN 64
+#define PENDING_MAX 128
+
+struct data {
+ char *saddr;
+ char *raddr;
+ long amount;
+};
+
+struct block {
+ struct data *data;
+ char hash[HASH_LEN + 1];
+ char prevhash[HASH_LEN + 1];
+ char tstmp[20];
+ int nonce;
+};
+
+struct blockchain {
+ struct block **blocks;
+ struct block *pending[PENDING_MAX];
+ size_t nblocks;
+ size_t npending;
+ int difficulty;
+ int reward;
+};
+
+static void transaction(const char *, const char *, long);
+static struct block *newblock(const char *, const char *, long, const char *);
+static char *calchash(struct block *);
+static void initchain(void);
+static void minepending(const char *);
+static void mineblock(struct block *);
+static int validchain(void);
+static struct block *lastblock(void);
+static long balance(const char *);
+static void printchain(void);
+static void cleanchain(void);
+static void *emalloc(size_t);
+static void die(const char *, ...);
+
+static struct blockchain *chain;
+
+static void
+transaction(const char *from, const char *to, long amount)
+{
+ if (chain->npending < PENDING_MAX)
+ chain->pending[chain->npending++] =
+ newblock(from, to, amount, NULL);
+ else
+ fprintf(stderr, "transaction array is full\n");
+}
+
+static struct block *
+newblock(const char *saddr, const char *raddr, long amount, const char *prevhash)
+{
+ struct block *b;
+ struct tm *tm;
+ time_t rtime;
+
+ b = emalloc(sizeof(struct block));
+ b->data = emalloc(sizeof(struct data));
+ b->data->saddr = strdup(saddr);
+ b->data->raddr = strdup(raddr);
+ b->data->amount = amount;
+ time(&rtime);
+ tm = localtime(&rtime);
+ strftime(b->tstmp, sizeof(b->tstmp), "%F %T", tm);
+ strcpy(b->prevhash, prevhash == NULL ? "" : prevhash);
+ strcpy(b->hash, calchash(b));
+ b->nonce = 0;
+
+ return b;
+}
+
+static char *
+calchash(struct block *b)
+{
+ SHA256_CTX sha256;
+ unsigned char hash[SHA256_DIGEST_LENGTH];
+ char buf[HASH_LEN + 19 + strlen(b->data->saddr) + strlen(b->data->raddr) + 10 + 10 + 1];
+ char *res;
+ int i = 0;
+
+ res = emalloc(HASH_LEN + 1);
+ sprintf(buf, "%s%s%s%s%ld%d",
+ b->prevhash, b->tstmp,
+ b->data->saddr, b->data->raddr, b->data->amount, b->nonce);
+
+ SHA256_Init(&sha256);
+ SHA256_Update(&sha256, buf, strlen(buf));
+ SHA256_Final(hash, &sha256);
+ for (; i < SHA256_DIGEST_LENGTH; i++)
+ sprintf(&res[i << 1], "%02x", hash[i]);
+ res[HASH_LEN] = '\0';
+
+ return res;
+}
+
+static void
+initchain(void)
+{
+ chain = emalloc(sizeof(struct blockchain));
+ chain->blocks = emalloc(sizeof(struct block *));
+ memset(chain->pending, 0, sizeof(chain->pending));
+ chain->nblocks = 1;
+ chain->npending = 0;
+ chain->difficulty = 4;
+ chain->reward = 100;
+ chain->blocks[0] = newblock("Genesis", "Genesis", 0, "0");
+}
+
+static void
+minepending(const char *rewaddr)
+{
+ struct block *b, *last;
+ int i = 0;
+
+ if (chain->npending < 1)
+ return;
+
+ if ((chain->blocks = realloc(chain->blocks,
+ sizeof(struct block *) * (chain->nblocks + chain->npending + 1))) == NULL)
+ die("realloc:");
+
+ for (; i < chain->npending; i++) {
+ b = chain->pending[i];
+ last = lastblock();
+ if (!strcmp(b->prevhash, ""))
+ strcpy(b->prevhash, last->hash);
+ mineblock(b);
+ chain->blocks[chain->nblocks++] = b;
+ }
+ chain->npending = 0;
+ memset(chain->pending, 0, sizeof(chain->pending));
+ transaction("Mining Award", rewaddr, chain->reward);
+}
+
+static void
+mineblock(struct block *b)
+{
+ int d = chain->difficulty, i = 0;
+ char z[d];
+
+ for (; i < d; i++)
+ z[i] = '0';
+ while (strncmp(b->hash, z, d)) {
+ b->nonce++;
+ strcpy(b->hash, calchash(b));
+ }
+ printf("struct block mined: %s\n", b->hash);
+}
+static struct block *
+lastblock(void)
+{
+ return chain->blocks[chain->nblocks - 1];
+}
+
+static int
+validchain(void)
+{
+ int i = 0;
+
+ for (; i < chain->nblocks; i++) {
+ if (i != 0 && strcmp(chain->blocks[i]->prevhash,
+ chain->blocks[i-1]->hash))
+ return 0;
+ if (i != 0 && strcmp(chain->blocks[i]->hash,
+ calchash(chain->blocks[i])))
+ return 0;
+ }
+
+ return 1;
+}
+
+static long
+balance(const char *addr)
+{
+ long bal = 0;
+ int i;
+
+ for (i = 0; i < chain->nblocks; i++) {
+ if (!strcmp(chain->blocks[i]->data->saddr, addr))
+ bal -= chain->blocks[i]->data->amount;
+ if (!strcmp(chain->blocks[i]->data->raddr, addr))
+ bal += chain->blocks[i]->data->amount;
+ }
+
+ return bal;
+}
+
+static void
+printchain(void)
+{
+ int i = 0;
+
+ for (; i < chain->nblocks; i++) {
+ printf("HASH: %s\n", chain->blocks[i]->hash);
+ printf("PREVHASH: %s\n", chain->blocks[i]->prevhash);
+ printf("TIMESTAMP: %s\n", chain->blocks[i]->tstmp);
+ printf("DATA:\n");
+ printf(" FROM: %s\n", chain->blocks[i]->data->saddr);
+ printf(" TO: %s\n", chain->blocks[i]->data->raddr);
+ printf(" AMOUNT: %ld\n", chain->blocks[i]->data->amount);
+ printf("\n");
+ }
+}
+
+static void
+cleanchain(void)
+{
+ int i = 0;
+
+ for (; i < chain->nblocks; i++) {
+ free(chain->blocks[i]->data->saddr);
+ free(chain->blocks[i]->data->raddr);
+ free(chain->blocks[i]->data);
+ free(chain->blocks[i]);
+ }
+ free(chain->blocks);
+ free(chain);
+}
+
+static void *
+emalloc(size_t nb)
+{
+ void *p;
+
+ if ((p = malloc(nb)) == NULL)
+ die("malloc:");
+
+ return p;
+}
+
+static void
+die(const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+
+ if (fmt[0] && fmt[strlen(fmt)-1] == ':') {
+ fputc(' ', stderr);
+ perror(NULL);
+ } else
+ fputc('\n', stderr);
+
+ exit(EXIT_FAILURE);
+}
+
+int
+main(int argc, char *argv[])
+{
+ initchain();
+
+ transaction("Christos", "Lol", 1000);
+ transaction("N", "Lol", 1000);
+ minepending("miner");
+
+ printchain();
+ printf("Valid chain: %s\n", validchain() ? "Yes" : "No");
+ printf("Lol's balance: %ld\n", balance("Lol"));
+
+ cleanchain();
+
+ return 0;
+}