diff options
| author | Jack Humbert <jack.humb@gmail.com> | 2017-08-30 11:27:52 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2017-08-30 11:27:52 -0400 |
| commit | a729d852fe502d62e09bab1a31f49b738f8892e4 (patch) | |
| tree | 5caaf1de34f2d46afa2b5991a7ea4e0663f2db85 | |
| parent | da7aece043759f055063cb5d20bea4109891f917 (diff) | |
| parent | 4a9e16b39499294a3ccc0e52e97696cab5d77668 (diff) | |
| download | qmk_firmware-a729d852fe502d62e09bab1a31f49b738f8892e4.tar.gz qmk_firmware-a729d852fe502d62e09bab1a31f49b738f8892e4.zip | |
Merge pull request #1574 from danamlund/master
New clueboard keymap that is a tetris game
| -rw-r--r-- | keyboards/clueboard/keymaps/tetris/Makefile | 1 | ||||
| -rw-r--r-- | keyboards/clueboard/keymaps/tetris/keymap.c | 209 | ||||
| -rw-r--r-- | keyboards/clueboard/keymaps/tetris/readme.md | 33 | ||||
| -rw-r--r-- | keyboards/clueboard/keymaps/tetris/tetris_text.c | 505 | ||||
| -rw-r--r-- | keyboards/clueboard/keymaps/tetris/tetris_text.h | 45 |
5 files changed, 793 insertions, 0 deletions
diff --git a/keyboards/clueboard/keymaps/tetris/Makefile b/keyboards/clueboard/keymaps/tetris/Makefile new file mode 100644 index 000000000..461353129 --- /dev/null +++ b/keyboards/clueboard/keymaps/tetris/Makefile | |||
| @@ -0,0 +1 @@ | |||
| SRC = tetris_text.c | |||
diff --git a/keyboards/clueboard/keymaps/tetris/keymap.c b/keyboards/clueboard/keymaps/tetris/keymap.c new file mode 100644 index 000000000..c5a16b158 --- /dev/null +++ b/keyboards/clueboard/keymaps/tetris/keymap.c | |||
| @@ -0,0 +1,209 @@ | |||
| 1 | #include "clueboard.h" | ||
| 2 | #include "tetris_text.h" | ||
| 3 | |||
| 4 | // Helpful defines | ||
| 5 | #define GRAVE_MODS (MOD_BIT(KC_LSHIFT)|MOD_BIT(KC_RSHIFT)|MOD_BIT(KC_LGUI)|MOD_BIT(KC_RGUI)|MOD_BIT(KC_LALT)|MOD_BIT(KC_RALT)) | ||
| 6 | #define _______ KC_TRNS | ||
| 7 | |||
| 8 | // Each layer gets a name for readability, which is then used in the keymap matrix below. | ||
| 9 | // The underscores don't mean anything - you can have a layer called STUFF or any other name. | ||
| 10 | // Layer names don't all need to be of the same length, obviously, and you can also skip them | ||
| 11 | // entirely and just use numbers. | ||
| 12 | #define _BL 0 | ||
| 13 | #define _FL 1 | ||
| 14 | #define _CL 2 | ||
| 15 | |||
| 16 | const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { | ||
| 17 | /* Keymap _BL: Base Layer (Default Layer) | ||
| 18 | */ | ||
| 19 | [_BL] = KEYMAP( | ||
| 20 | F(0), KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINS, KC_EQL, KC_GRV, KC_BSPC, KC_PGUP, \ | ||
| 21 | KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_LBRC, KC_RBRC, KC_BSLS, KC_PGDN, \ | ||
| 22 | KC_CAPS, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT, KC_NUHS, KC_ENT, \ | ||
| 23 | KC_LSFT, KC_NUBS, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_RO, KC_RSFT, KC_UP, \ | ||
| 24 | KC_LCTL, KC_LGUI, KC_LALT, KC_MHEN, KC_SPC,KC_SPC, KC_HENK, KC_RALT, KC_RCTL, MO(_FL), KC_LEFT, KC_DOWN, KC_RGHT), | ||
| 25 | |||
| 26 | /* Keymap _FL: Function Layer | ||
| 27 | */ | ||
| 28 | [_FL] = KEYMAP( | ||
| 29 | KC_GRV, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, _______, KC_DEL, BL_STEP, \ | ||
| 30 | _______, _______, _______,_______,_______,F(1) ,_______,_______,KC_PSCR,KC_SLCK, KC_PAUS, _______, _______, _______, _______, \ | ||
| 31 | _______, _______, MO(_CL),_______,_______,_______,_______,_______,_______,_______, _______, _______, _______, _______, \ | ||
| 32 | _______, _______, _______,_______,_______,_______,_______,_______,_______,_______, _______, _______, _______, _______, KC_PGUP, \ | ||
| 33 | _______, _______, _______, _______, _______,_______, _______, _______, _______, MO(_FL), KC_HOME, KC_PGDN, KC_END), | ||
| 34 | |||
| 35 | /* Keymap _CL: Control layer | ||
| 36 | */ | ||
| 37 | [_CL] = KEYMAP( | ||
| 38 | _______, _______, _______,_______,_______,_______,_______,_______,_______,_______, _______, _______, _______, _______, RGB_TOG, RGB_VAI, \ | ||
| 39 | _______, _______, _______,_______,RESET, _______,_______,_______,_______,_______, _______, _______, _______, _______, RGB_VAD, \ | ||
| 40 | _______, _______, MO(_CL),_______,_______,_______,_______,_______,_______,_______, _______, _______, _______, _______, \ | ||
| 41 | MO(_FL), _______, _______,_______,_______,_______,_______,_______,_______,_______, _______, _______, _______, MO(_FL), RGB_SAI, \ | ||
| 42 | _______, _______, _______,_______, RGB_MOD, RGB_MOD, _______, _______, _______, _______, RGB_HUD, RGB_SAD, RGB_HUI), | ||
| 43 | }; | ||
| 44 | |||
| 45 | /* This is a list of user defined functions. F(N) corresponds to item N | ||
| 46 | of this list. | ||
| 47 | */ | ||
| 48 | const uint16_t PROGMEM fn_actions[] = { | ||
| 49 | [0] = ACTION_FUNCTION(0), // Calls action_function() | ||
| 50 | [1] = ACTION_FUNCTION(1) | ||
| 51 | }; | ||
| 52 | |||
| 53 | static uint8_t tetris_key_presses = 0; | ||
| 54 | static uint16_t tetris_timer = 0; | ||
| 55 | static uint8_t tetris_running = 0; | ||
| 56 | static int tetris_keypress = 0; | ||
| 57 | |||
| 58 | void action_function(keyrecord_t *record, uint8_t id, uint8_t opt) { | ||
| 59 | static uint8_t mods_pressed; | ||
| 60 | static bool mod_flag; | ||
| 61 | |||
| 62 | switch (id) { | ||
| 63 | case 0: | ||
| 64 | // clueboard specific hook to make escape quite tetris | ||
| 65 | if (tetris_running) { | ||
| 66 | tetris_running = 0; | ||
| 67 | return; | ||
| 68 | } | ||
| 69 | |||
| 70 | /* Handle the combined Grave/Esc key | ||
| 71 | */ | ||
| 72 | mods_pressed = get_mods()&GRAVE_MODS; // Check to see what mods are pressed | ||
| 73 | |||
| 74 | if (record->event.pressed) { | ||
| 75 | /* The key is being pressed. | ||
| 76 | */ | ||
| 77 | if (mods_pressed) { | ||
| 78 | mod_flag = true; | ||
| 79 | add_key(KC_GRV); | ||
| 80 | send_keyboard_report(); | ||
| 81 | } else { | ||
| 82 | add_key(KC_ESC); | ||
| 83 | send_keyboard_report(); | ||
| 84 | } | ||
| 85 | } else { | ||
| 86 | /* The key is being released. | ||
| 87 | */ | ||
| 88 | if (mod_flag) { | ||
| 89 | mod_flag = false; | ||
| 90 | del_key(KC_GRV); | ||
| 91 | send_keyboard_report(); | ||
| 92 | } else { | ||
| 93 | del_key(KC_ESC); | ||
| 94 | send_keyboard_report(); | ||
| 95 | } | ||
| 96 | } | ||
| 97 | break; | ||
| 98 | case 1: | ||
| 99 | if (record->event.pressed) { | ||
| 100 | tetris_running = 1; | ||
| 101 | tetris_timer = 0; | ||
| 102 | tetris_keypress = 0; | ||
| 103 | // set randomness using total number of key presses | ||
| 104 | tetris_start(tetris_key_presses); | ||
| 105 | } | ||
| 106 | break; | ||
| 107 | } | ||
| 108 | } | ||
| 109 | |||
| 110 | /* | ||
| 111 | * Set up tetris | ||
| 112 | */ | ||
| 113 | |||
| 114 | bool process_record_user(uint16_t keycode, keyrecord_t *record) { | ||
| 115 | if (record->event.pressed) { | ||
| 116 | tetris_key_presses++; | ||
| 117 | } | ||
| 118 | |||
| 119 | if (tetris_running && record->event.pressed) { | ||
| 120 | tetris_keypress = 0; | ||
| 121 | switch (keycode) { | ||
| 122 | case KC_UP: tetris_keypress = 1; break; | ||
| 123 | case KC_LEFT: tetris_keypress = 2; break; | ||
| 124 | case KC_DOWN: tetris_keypress = 3; break; | ||
| 125 | case KC_RIGHT: tetris_keypress = 4; break; | ||
| 126 | // Make ESC stop tetris (on keyboards other than clueboard) | ||
| 127 | // case KC_ESC: tetris_running = 0; return false; | ||
| 128 | } | ||
| 129 | if (tetris_keypress != 0) { | ||
| 130 | return false; | ||
| 131 | } | ||
| 132 | } | ||
| 133 | |||
| 134 | return true; | ||
| 135 | } | ||
| 136 | |||
| 137 | // Runs constantly in the background, in a loop. | ||
| 138 | void matrix_scan_user(void) { | ||
| 139 | if (tetris_running) { | ||
| 140 | tetris_timer++; | ||
| 141 | if (tetris_timer > 1000) { | ||
| 142 | // every 1000 times this is run is about 100 ms. | ||
| 143 | if (!tetris_tick(100)) { | ||
| 144 | // game over | ||
| 145 | tetris_running = 0; | ||
| 146 | } | ||
| 147 | tetris_timer = 0; | ||
| 148 | } | ||
| 149 | } | ||
| 150 | } | ||
| 151 | |||
| 152 | void send_keycode(uint16_t keycode) { | ||
| 153 | register_code(keycode); | ||
| 154 | unregister_code(keycode); | ||
| 155 | } | ||
| 156 | |||
| 157 | void send_keycode_shift(uint16_t keycode) { | ||
| 158 | register_code(KC_LSFT); | ||
| 159 | register_code(keycode); | ||
| 160 | unregister_code(keycode); | ||
| 161 | unregister_code(KC_LSFT); | ||
| 162 | } | ||
| 163 | |||
| 164 | void tetris_send_up(void) { | ||
| 165 | send_keycode(KC_UP); | ||
| 166 | } | ||
| 167 | void tetris_send_left(void) { | ||
| 168 | send_keycode(KC_LEFT); | ||
| 169 | } | ||
| 170 | void tetris_send_down(void) { | ||
| 171 | send_keycode(KC_DOWN); | ||
| 172 | } | ||
| 173 | void tetris_send_right(void) { | ||
| 174 | send_keycode(KC_RGHT); | ||
| 175 | } | ||
| 176 | void tetris_send_backspace(void) { | ||
| 177 | send_keycode(KC_BSPC); | ||
| 178 | } | ||
| 179 | void tetris_send_delete(void) { | ||
| 180 | send_keycode(KC_DEL); | ||
| 181 | } | ||
| 182 | |||
| 183 | void tetris_send_string(const char *s) { | ||
| 184 | for (int i = 0; s[i] != 0; i++) { | ||
| 185 | if (s[i] >= 'a' && s[i] <= 'z') { | ||
| 186 | send_keycode(KC_A + (s[i] - 'a')); | ||
| 187 | } else if (s[i] >= 'A' && s[i] <= 'Z') { | ||
| 188 | send_keycode_shift(KC_A + (s[i] - 'A')); | ||
| 189 | } else if (s[i] >= '1' && s[i] <= '9') { | ||
| 190 | send_keycode(KC_1 + (s[i] - '1')); | ||
| 191 | } else { | ||
| 192 | switch (s[i]) { | ||
| 193 | case ' ': send_keycode(KC_SPACE); break; | ||
| 194 | case '.': send_keycode(KC_DOT); break; | ||
| 195 | case '0': send_keycode(KC_0); break; | ||
| 196 | } | ||
| 197 | } | ||
| 198 | } | ||
| 199 | } | ||
| 200 | |||
| 201 | void tetris_send_newline(void) { | ||
| 202 | send_keycode(KC_ENT); | ||
| 203 | } | ||
| 204 | |||
| 205 | int tetris_get_keypress(void) { | ||
| 206 | int out = tetris_keypress; | ||
| 207 | tetris_keypress = 0; | ||
| 208 | return out; | ||
| 209 | } | ||
diff --git a/keyboards/clueboard/keymaps/tetris/readme.md b/keyboards/clueboard/keymaps/tetris/readme.md new file mode 100644 index 000000000..20e97fb19 --- /dev/null +++ b/keyboards/clueboard/keymaps/tetris/readme.md | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | Default layout but with a tetris game | ||
| 2 | ===================================== | ||
| 3 | Tetris works by outputting key-presses to make ascii-art in a regular text editor. | ||
| 4 | It reads key presses to rotate and move the bricks like a regular tetris game. | ||
| 5 | |||
| 6 | Example | ||
| 7 | ======= | ||
| 8 |  | ||
| 9 | |||
| 10 | Usage | ||
| 11 | ===== | ||
| 12 | 1) Open a default text editor | ||
| 13 | 2) Press the tetris button (Fn + t) | ||
| 14 | 3) Play tetris | ||
| 15 | |||
| 16 | It makes ascii-art by sending keycodes: left, right, up, down, qwerty characters, and numbers. | ||
| 17 | |||
| 18 | Problems | ||
| 19 | ======== | ||
| 20 | Drawing ascii-art is too slow to make a pleasant playing experience. | ||
| 21 | While drawing ascii-art, the keyboard does not record key-presses, so its pretty unresponsive. | ||
| 22 | |||
| 23 | Adds 5000 bytes to the hex file. | ||
| 24 | |||
| 25 | Implement in other keyboards | ||
| 26 | ============================ | ||
| 27 | - Copy-paste the files tetris_text.c and tetrix_text.h to your keymap folder. | ||
| 28 | - Add/update your-keyboard/your-keymap/Makefile to include ``SRC = tetris_text.c`` | ||
| 29 | - Copy-paste the tetris-related code from this keymap.c to yours. | ||
| 30 | - Set a key to trigger F(1) to start tetris mode. | ||
| 31 | - Its also a good idea to set a key to stop tetris, here its escape. | ||
| 32 | |||
| 33 | You can find a simple tetris keyboard definition at <https://github.com/danamlund/meckb_tetris/> | ||
diff --git a/keyboards/clueboard/keymaps/tetris/tetris_text.c b/keyboards/clueboard/keymaps/tetris/tetris_text.c new file mode 100644 index 000000000..1376ead00 --- /dev/null +++ b/keyboards/clueboard/keymaps/tetris/tetris_text.c | |||
| @@ -0,0 +1,505 @@ | |||
| 1 | /* Copyright 2017 Dan Amlund Thomsen | ||
| 2 | * | ||
| 3 | * This program is free software: you can redistribute it and/or modify | ||
| 4 | * it under the terms of the GNU General Public License as published by | ||
| 5 | * the Free Software Foundation, either version 2 of the License, or | ||
| 6 | * (at your option) any later version. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, | ||
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | * GNU General Public License for more details. | ||
| 12 | * | ||
| 13 | * You should have received a copy of the GNU General Public License | ||
| 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 15 | */ | ||
| 16 | #include <stdio.h> | ||
| 17 | #include <stdlib.h> | ||
| 18 | #include <stdarg.h> | ||
| 19 | #include <stdint.h> | ||
| 20 | |||
| 21 | #include "tetris_text.h" | ||
| 22 | |||
| 23 | static char empty_piece[7][7] = { { 0, 0, 0, 0, 0, 0, 0 }, | ||
| 24 | { 0, 0, 0, 0, 0, 0, 0 }, | ||
| 25 | { 0, 0, 0, 0, 0, 0, 0 }, | ||
| 26 | { 0, 0, 0, 0, 0, 0, 0 }, | ||
| 27 | { 0, 0, 0, 0, 0, 0, 0 }, | ||
| 28 | { 0, 0, 0, 0, 0, 0, 0 }, | ||
| 29 | { 0, 0, 0, 0, 0, 0, 0 } }; | ||
| 30 | |||
| 31 | static char temp_piece[7][7]; | ||
| 32 | |||
| 33 | static int curx = 0; | ||
| 34 | static int cury = 0; | ||
| 35 | |||
| 36 | static void clear_piece(char piece[7][7]) { | ||
| 37 | for (int y = 0; y < 7; y++) { | ||
| 38 | for (int x = 0; x < 7; x++) { | ||
| 39 | piece[x][y] = 0; | ||
| 40 | } | ||
| 41 | } | ||
| 42 | } | ||
| 43 | |||
| 44 | static void copy_piece_from_to(char from[7][7], char to[7][7]) { | ||
| 45 | for (int y = 0; y < 7; y++) { | ||
| 46 | for (int x = 0; x < 7; x++) { | ||
| 47 | to[x][y] = from[x][y]; | ||
| 48 | } | ||
| 49 | } | ||
| 50 | } | ||
| 51 | |||
| 52 | static void rotate_piece(char piece[7][7]) { | ||
| 53 | // transpose | ||
| 54 | for (int y = 0; y < 7; y++) { | ||
| 55 | for (int x = y + 1; x < 7; x++) { | ||
| 56 | char tmp = piece[y][x]; | ||
| 57 | piece[y][x] = piece[x][y]; | ||
| 58 | piece[x][y] = tmp; | ||
| 59 | } | ||
| 60 | } | ||
| 61 | |||
| 62 | // reverse rows | ||
| 63 | for (int y = 0; y < 7; y++) { | ||
| 64 | for (int x = 0; x < 3; x++) { | ||
| 65 | char tmp = piece[y][6 - x]; | ||
| 66 | piece[y][6 - x] = piece[y][x]; | ||
| 67 | piece[y][x] = tmp; | ||
| 68 | } | ||
| 69 | } | ||
| 70 | } | ||
| 71 | |||
| 72 | static char get_shape_char(int shape) { | ||
| 73 | switch (shape) { | ||
| 74 | case 0: return 'I'; | ||
| 75 | case 1: return 'J'; | ||
| 76 | case 2: return 'L'; | ||
| 77 | case 3: return 'O'; | ||
| 78 | case 4: return 'S'; | ||
| 79 | case 5: return 'T'; | ||
| 80 | case 6: return 'Z'; | ||
| 81 | } | ||
| 82 | return 'Q'; | ||
| 83 | } | ||
| 84 | |||
| 85 | static void set_piece(char piece[7][7], int shape, int rotation) { | ||
| 86 | clear_piece(piece); | ||
| 87 | switch (shape) { | ||
| 88 | case 0: | ||
| 89 | if (rotation % 2 == 0) { | ||
| 90 | // xxXx | ||
| 91 | piece[3][1] = 1; | ||
| 92 | piece[3][2] = 1; | ||
| 93 | piece[3][3] = 1; | ||
| 94 | piece[3][4] = 1; | ||
| 95 | } else { | ||
| 96 | // x | ||
| 97 | // x | ||
| 98 | // X | ||
| 99 | // x | ||
| 100 | piece[1][3] = 1; | ||
| 101 | piece[2][3] = 1; | ||
| 102 | piece[3][3] = 1; | ||
| 103 | piece[4][3] = 1; | ||
| 104 | } | ||
| 105 | break; | ||
| 106 | case 1: | ||
| 107 | // xXx | ||
| 108 | // x | ||
| 109 | piece[3][2] = 1; | ||
| 110 | piece[3][3] = 1; | ||
| 111 | piece[3][4] = 1; | ||
| 112 | piece[4][4] = 1; | ||
| 113 | for (int i = 0; i < rotation; i++) { | ||
| 114 | rotate_piece(piece); | ||
| 115 | } | ||
| 116 | break; | ||
| 117 | case 2: | ||
| 118 | // xXx | ||
| 119 | // x | ||
| 120 | piece[3][2] = 1; | ||
| 121 | piece[3][3] = 1; | ||
| 122 | piece[3][4] = 1; | ||
| 123 | piece[4][2] = 1; | ||
| 124 | for (int i = 0; i < rotation; i++) { | ||
| 125 | rotate_piece(piece); | ||
| 126 | } | ||
| 127 | break; | ||
| 128 | case 3: | ||
| 129 | // xX | ||
| 130 | // xx | ||
| 131 | piece[3][2] = 1; | ||
| 132 | piece[3][3] = 1; | ||
| 133 | piece[4][2] = 1; | ||
| 134 | piece[4][3] = 1; | ||
| 135 | break; | ||
| 136 | case 4: | ||
| 137 | if (rotation % 2 == 0) { | ||
| 138 | // xX | ||
| 139 | // xx | ||
| 140 | piece[3][2] = 1; | ||
| 141 | piece[3][3] = 1; | ||
| 142 | piece[4][3] = 1; | ||
| 143 | piece[4][4] = 1; | ||
| 144 | } else { | ||
| 145 | // x | ||
| 146 | // xX | ||
| 147 | // x | ||
| 148 | piece[2][3] = 1; | ||
| 149 | piece[3][2] = 1; | ||
| 150 | piece[3][3] = 1; | ||
| 151 | piece[4][2] = 1; | ||
| 152 | } | ||
| 153 | break; | ||
| 154 | case 5: | ||
| 155 | // xXx | ||
| 156 | // x | ||
| 157 | piece[3][2] = 1; | ||
| 158 | piece[3][3] = 1; | ||
| 159 | piece[3][4] = 1; | ||
| 160 | piece[4][3] = 1; | ||
| 161 | for (int i = 0; i < rotation; i++) { | ||
| 162 | rotate_piece(piece); | ||
| 163 | } | ||
| 164 | break; | ||
| 165 | case 6: | ||
| 166 | if (rotation % 2 == 0) { | ||
| 167 | // Xx | ||
| 168 | // xx | ||
| 169 | piece[3][3] = 1; | ||
| 170 | piece[3][4] = 1; | ||
| 171 | piece[4][2] = 1; | ||
| 172 | piece[4][3] = 1; | ||
| 173 | } else { | ||
| 174 | // x | ||
| 175 | // Xx | ||
| 176 | // x | ||
| 177 | piece[2][3] = 1; | ||
| 178 | piece[3][3] = 1; | ||
| 179 | piece[3][4] = 1; | ||
| 180 | piece[4][4] = 1; | ||
| 181 | } | ||
| 182 | break; | ||
| 183 | } | ||
| 184 | } | ||
| 185 | |||
| 186 | static void send_deletes(int deletes) { | ||
| 187 | for (int i = 0; i < deletes; i++) { | ||
| 188 | tetris_send_delete(); | ||
| 189 | } | ||
| 190 | } | ||
| 191 | |||
| 192 | static void send_backspaces(int backspaces) { | ||
| 193 | for (int i = 0; i < backspaces; i++) { | ||
| 194 | tetris_send_backspace(); | ||
| 195 | curx--; | ||
| 196 | } | ||
| 197 | } | ||
| 198 | |||
| 199 | static void send_goto_xy(int x, int y) { | ||
| 200 | while (curx < x) { | ||
| 201 | tetris_send_right(); | ||
| 202 | curx++; | ||
| 203 | } | ||
| 204 | while (curx > x) { | ||
| 205 | tetris_send_left(); | ||
| 206 | curx--; | ||
| 207 | } | ||
| 208 | while (cury < y) { | ||
| 209 | tetris_send_down(); | ||
| 210 | cury++; | ||
| 211 | } | ||
| 212 | while (cury > y) { | ||
| 213 | tetris_send_up(); | ||
| 214 | cury--; | ||
| 215 | } | ||
| 216 | } | ||
| 217 | |||
| 218 | static void draw_row(char c, const char oldrow[7], const char newrow[7], int x, int y) { | ||
| 219 | char str[2] = { c, 0 }; | ||
| 220 | char row_is_del[7] = { 0 }; | ||
| 221 | int first = -1; | ||
| 222 | int last = -1; | ||
| 223 | for (int px = 0; px < 7; px++) { | ||
| 224 | if (oldrow[px] && !newrow[px]) { | ||
| 225 | row_is_del[px] = 1; | ||
| 226 | } | ||
| 227 | if (newrow[px] || oldrow[px]) { | ||
| 228 | if (first == -1) first = px; | ||
| 229 | last = px; | ||
| 230 | } | ||
| 231 | } | ||
| 232 | |||
| 233 | if (first >= 0) { | ||
| 234 | if (curx > x + last + 1) { | ||
| 235 | send_goto_xy(x + last + 1, cury); | ||
| 236 | } | ||
| 237 | if (curx < x + first) { | ||
| 238 | send_goto_xy(x + first, cury); | ||
| 239 | } | ||
| 240 | send_goto_xy(curx, y); | ||
| 241 | send_deletes((x + last + 1) - curx); | ||
| 242 | send_backspaces(curx - (x + first)); | ||
| 243 | for (int i = first; i <= last; i++) { | ||
| 244 | if (row_is_del[i]) { | ||
| 245 | tetris_send_string("."); | ||
| 246 | } else { | ||
| 247 | tetris_send_string(str); | ||
| 248 | } | ||
| 249 | curx++; | ||
| 250 | } | ||
| 251 | } | ||
| 252 | } | ||
| 253 | |||
| 254 | static void move_piece_from_to(char from[7][7], char to[7][7], int xadd, int yadd) { | ||
| 255 | for (int y = 0; y < 7; y++) { | ||
| 256 | for (int x = 0; x < 7; x++) { | ||
| 257 | if (x + xadd >= 0 && x + xadd < 7 && y + yadd >= 0 && y + yadd < 7) { | ||
| 258 | to[y][x] = from[y + yadd][x + xadd]; | ||
| 259 | } else { | ||
| 260 | to[y][x] = 0; | ||
| 261 | } | ||
| 262 | } | ||
| 263 | } | ||
| 264 | } | ||
| 265 | |||
| 266 | static void draw_piece(char c, int x, int y, char oldpiece[7][7], char piece[7][7]) { | ||
| 267 | for (int py = 0; py < 7; py++) { | ||
| 268 | draw_row(c, oldpiece[py], piece[py], x, y + py); | ||
| 269 | } | ||
| 270 | } | ||
| 271 | |||
| 272 | static void draw_piece_moved(char c, int x, int y, char piece[7][7], int oldxadd, int oldyadd) { | ||
| 273 | move_piece_from_to(piece, temp_piece, oldxadd, oldyadd); | ||
| 274 | draw_piece(c, x, y, temp_piece, piece); | ||
| 275 | } | ||
| 276 | |||
| 277 | static int is_piece_hitting(char board[20][10], char piece[7][7], int x, int y) { | ||
| 278 | for (int py = 0; py < 7; py++) { | ||
| 279 | for (int px = 0; px < 7; px++) { | ||
| 280 | if (piece[py][px] && | ||
| 281 | (px + x >= 10 || px + x < 0 | ||
| 282 | || py + y >= 20 || py + y < 0 | ||
| 283 | || board[py + y][px + x])) { | ||
| 284 | return 1; | ||
| 285 | } | ||
| 286 | } | ||
| 287 | } | ||
| 288 | return 0; | ||
| 289 | } | ||
| 290 | |||
| 291 | static void add_piece_to_board(char piece[7][7], char board[20][10], int x, int y) { | ||
| 292 | for (int py = 0; py < 7; py++) { | ||
| 293 | for (int px = 0; px < 7; px++) { | ||
| 294 | if (piece[py][px]) { | ||
| 295 | board[py + y][px + x] = piece[py][px]; | ||
| 296 | } | ||
| 297 | } | ||
| 298 | } | ||
| 299 | } | ||
| 300 | |||
| 301 | static void draw_board_line(void) { | ||
| 302 | //send_string("l l"); | ||
| 303 | tetris_send_string("l..........l"); | ||
| 304 | tetris_send_newline(); | ||
| 305 | } | ||
| 306 | static void init(void) { | ||
| 307 | for (int i = 0; i < 20; i++) { | ||
| 308 | draw_board_line(); | ||
| 309 | } | ||
| 310 | tetris_send_string("doooooooooob"); | ||
| 311 | curx = 12; | ||
| 312 | cury = 20; | ||
| 313 | } | ||
| 314 | |||
| 315 | static int get_piece_min_y(char piece[7][7]) { | ||
| 316 | for (int y = 0; y < 7; y++) { | ||
| 317 | for (int x = 0; x < 7; x++) { | ||
| 318 | if (piece[y][x]) | ||
| 319 | return y; | ||
| 320 | } | ||
| 321 | } | ||
| 322 | return 0; | ||
| 323 | } | ||
| 324 | |||
| 325 | static int clear_lines(char board[20][10]) { | ||
| 326 | int cleared_lines = 0; | ||
| 327 | for (int y = 19; y >= 0; y--) { | ||
| 328 | char isfull = 1; | ||
| 329 | for (int x = 0; x < 10; x++) { | ||
| 330 | if (!board[y][x]) { | ||
| 331 | isfull = 0; | ||
| 332 | } | ||
| 333 | } | ||
| 334 | if (isfull) { | ||
| 335 | // delete clear line | ||
| 336 | send_goto_xy(12, y); | ||
| 337 | send_backspaces(12); // delete line contents | ||
| 338 | // delete newline | ||
| 339 | tetris_send_backspace(); | ||
| 340 | cury--; | ||
| 341 | curx = 12; | ||
| 342 | cleared_lines++; | ||
| 343 | } else { | ||
| 344 | if (cleared_lines > 0) { | ||
| 345 | // move cleared lines down on board | ||
| 346 | for (int x = 0; x < 10; x++) { | ||
| 347 | board[y + cleared_lines][x] = board[y][x]; | ||
| 348 | } | ||
| 349 | } | ||
| 350 | } | ||
| 351 | } | ||
| 352 | // clear cleared top lines | ||
| 353 | for (int y = 0; y < cleared_lines; y++) { | ||
| 354 | for (int x = 0; x < 10; x++) { | ||
| 355 | board[y][x] = 0; | ||
| 356 | } | ||
| 357 | } | ||
| 358 | if (cleared_lines > 0) { | ||
| 359 | send_goto_xy(0, 0); | ||
| 360 | for (int i = 0; i < cleared_lines; i++) { | ||
| 361 | draw_board_line(); | ||
| 362 | curx = 0; | ||
| 363 | cury++; | ||
| 364 | } | ||
| 365 | } | ||
| 366 | return cleared_lines; | ||
| 367 | } | ||
| 368 | |||
| 369 | static uint8_t myrandom(uint8_t seed) { | ||
| 370 | uint8_t out = seed >> 1; | ||
| 371 | if (seed & 1) { | ||
| 372 | out = out ^ 0xB8; | ||
| 373 | } | ||
| 374 | return out; | ||
| 375 | } | ||
| 376 | |||
| 377 | static char piece[7][7]; | ||
| 378 | static char board[20][10]; | ||
| 379 | static uint8_t r; | ||
| 380 | static int score; | ||
| 381 | static int x; | ||
| 382 | static int y; | ||
| 383 | static int shape; | ||
| 384 | static int rotation; | ||
| 385 | static int time; | ||
| 386 | static int next_down; | ||
| 387 | static int down_delay; | ||
| 388 | static int first_run; | ||
| 389 | static int game_over; | ||
| 390 | |||
| 391 | void tetris_start(uint8_t seed) { | ||
| 392 | for (int y = 0; y < 20; y++) { | ||
| 393 | for (int x = 0; x < 10; x++) { | ||
| 394 | board[y][x] = 0; | ||
| 395 | } | ||
| 396 | } | ||
| 397 | |||
| 398 | clear_piece(piece); | ||
| 399 | |||
| 400 | init(); | ||
| 401 | |||
| 402 | game_over = 0; | ||
| 403 | |||
| 404 | r = seed; | ||
| 405 | score = 0; | ||
| 406 | |||
| 407 | copy_piece_from_to(empty_piece, piece); | ||
| 408 | x = 0; | ||
| 409 | y = 0; | ||
| 410 | shape = 0; | ||
| 411 | rotation = 0; | ||
| 412 | time = 0; | ||
| 413 | next_down = 0; | ||
| 414 | down_delay = -1; | ||
| 415 | first_run = 1; | ||
| 416 | } | ||
| 417 | |||
| 418 | int tetris_tick(int ms_since_previous_tick) { | ||
| 419 | if (game_over) { | ||
| 420 | return 0; | ||
| 421 | } | ||
| 422 | |||
| 423 | time += ms_since_previous_tick; | ||
| 424 | |||
| 425 | if (first_run || time > next_down) { | ||
| 426 | if (first_run || is_piece_hitting(board, piece, x, y + 1)) { | ||
| 427 | first_run = 0; | ||
| 428 | add_piece_to_board(piece, board, x, y); | ||
| 429 | |||
| 430 | score += clear_lines(board); | ||
| 431 | |||
| 432 | down_delay = 500 - score * 10; | ||
| 433 | if (down_delay < 100) { | ||
| 434 | down_delay = 100; | ||
| 435 | } | ||
| 436 | |||
| 437 | rotation = 0; | ||
| 438 | shape = r % 7; | ||
| 439 | r = myrandom(r); | ||
| 440 | set_piece(piece, shape, rotation); | ||
| 441 | |||
| 442 | x = 1; | ||
| 443 | y = - get_piece_min_y(piece); | ||
| 444 | draw_piece_moved(get_shape_char(shape), 1 + x, y, piece, 0, 0); | ||
| 445 | |||
| 446 | if (is_piece_hitting(board, piece, x, y)) { | ||
| 447 | game_over = 1; | ||
| 448 | send_goto_xy(12, 10); | ||
| 449 | tetris_send_string(" game over"); | ||
| 450 | tetris_send_down(); | ||
| 451 | tetris_send_string(" score "); | ||
| 452 | char tmp[10]; | ||
| 453 | sprintf(tmp, "%d", score); | ||
| 454 | tetris_send_string(tmp); | ||
| 455 | return 0; | ||
| 456 | } | ||
| 457 | } else { | ||
| 458 | y++; | ||
| 459 | draw_piece_moved(get_shape_char(shape), 1 + x, y, piece, 0, +1); | ||
| 460 | } | ||
| 461 | next_down = time + down_delay; | ||
| 462 | } else { | ||
| 463 | |||
| 464 | switch (tetris_get_keypress()) { | ||
| 465 | case 1: { // up | ||
| 466 | int oldrotation = rotation; | ||
| 467 | rotation = (rotation + 1) % 4; | ||
| 468 | copy_piece_from_to(piece, temp_piece); | ||
| 469 | set_piece(piece, shape, rotation); | ||
| 470 | if (is_piece_hitting(board, piece, x, y)) { | ||
| 471 | rotation = oldrotation; | ||
| 472 | set_piece(piece, shape, rotation); | ||
| 473 | } else { | ||
| 474 | draw_piece(get_shape_char(shape), 1 + x, y, temp_piece, piece); | ||
| 475 | } | ||
| 476 | break; | ||
| 477 | } | ||
| 478 | case 2: // left | ||
| 479 | if (!is_piece_hitting(board, piece, x - 1, y)) { | ||
| 480 | x--; | ||
| 481 | draw_piece_moved(get_shape_char(shape), 1 + x, y, piece, -1, 0); | ||
| 482 | } | ||
| 483 | break; | ||
| 484 | case 3: {// down | ||
| 485 | int starty = y; | ||
| 486 | while (!is_piece_hitting(board, piece, x, y + 1)) { | ||
| 487 | y++; | ||
| 488 | } | ||
| 489 | |||
| 490 | draw_piece(get_shape_char(shape), x + 1, starty, piece, empty_piece); | ||
| 491 | draw_piece(get_shape_char(shape), x + 1, y, empty_piece, piece); | ||
| 492 | |||
| 493 | next_down = time + down_delay; | ||
| 494 | break; | ||
| 495 | } | ||
| 496 | case 4: // right | ||
| 497 | if (!is_piece_hitting(board, piece, x + 1, y)) { | ||
| 498 | x++; | ||
| 499 | draw_piece_moved(get_shape_char(shape), 1 + x, y, piece, 1, 0); | ||
| 500 | } | ||
| 501 | break; | ||
| 502 | } | ||
| 503 | } | ||
| 504 | return 1; | ||
| 505 | } | ||
diff --git a/keyboards/clueboard/keymaps/tetris/tetris_text.h b/keyboards/clueboard/keymaps/tetris/tetris_text.h new file mode 100644 index 000000000..25b4177e0 --- /dev/null +++ b/keyboards/clueboard/keymaps/tetris/tetris_text.h | |||
| @@ -0,0 +1,45 @@ | |||
| 1 | /* Copyright 2017 Dan Amlund Thomsen | ||
| 2 | * | ||
| 3 | * This program is free software: you can redistribute it and/or modify | ||
| 4 | * it under the terms of the GNU General Public License as published by | ||
| 5 | * the Free Software Foundation, either version 2 of the License, or | ||
| 6 | * (at your option) any later version. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, | ||
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | * GNU General Public License for more details. | ||
| 12 | * | ||
| 13 | * You should have received a copy of the GNU General Public License | ||
| 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 15 | */ | ||
| 16 | #ifndef TETRIS_TEXT_H | ||
| 17 | #define TETRIS_TEXT_H | ||
| 18 | |||
| 19 | //// to implement | ||
| 20 | void tetris_send_up(void); | ||
| 21 | void tetris_send_left(void); | ||
| 22 | void tetris_send_down(void); | ||
| 23 | void tetris_send_right(void); | ||
| 24 | |||
| 25 | void tetris_send_backspace(void); | ||
| 26 | void tetris_send_delete(void); | ||
| 27 | |||
| 28 | void tetris_send_string(const char *s); | ||
| 29 | |||
| 30 | void tetris_send_newline(void); | ||
| 31 | |||
| 32 | // return = meaning | ||
| 33 | // 0 = no keys pressed | ||
| 34 | // 1 = up | ||
| 35 | // 2 = left | ||
| 36 | // 3 = down | ||
| 37 | // 4 = right | ||
| 38 | int tetris_get_keypress(void); | ||
| 39 | |||
| 40 | //// to call | ||
| 41 | void tetris_start(uint8_t seed); | ||
| 42 | // returns 0 when game is over | ||
| 43 | int tetris_tick(int ms_since_previous_tick); | ||
| 44 | |||
| 45 | #endif | ||
