├── .gitignore ├── README.md ├── LICENSE ├── doc └── vim-game-snake.txt └── plugin └── vim-game-snake.vim /.gitignore: -------------------------------------------------------------------------------- 1 | tags 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VimGameSnake 2 | 3 | version 0.1 4 | 5 | ![animated demonstration](https://cloud.githubusercontent.com/assets/1855714/25851103/5c40d8c2-34ff-11e7-93b2-e161d973e4c8.gif) 6 | 7 | ## How to play 8 | 9 | `:VimGameSnake` to Start 10 | `:echo g:VimSnakeScore` to view score 11 | 12 | h | j | k | l | c | q 13 | --- | --- | --- | --- | --- | --- 14 | ← |↓ |↑ |→ | end game | quit 15 | 16 | ## Installation 17 | 18 | ### VimPlug 19 | 20 | Place this in your .vimrc: 21 | 22 | > Plug 'johngrib/vim-game-snake' 23 | 24 | Then run the following in Vim: 25 | 26 | > :source % 27 | 28 | > :PlugInstall 29 | 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 이종립 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /doc/vim-game-snake.txt: -------------------------------------------------------------------------------- 1 | *vim-game-snake.txt* 2 | 3 | VimGameSnake~ 4 | 5 | ============================================================================== 6 | CONTENTS *vim-game-snake-contents* 7 | 8 | How to start ......... |vim-game-snake-start| 9 | How to play .......... |vim-game-snake-play| 10 | License .............. |vim-game-snake-license| 11 | Bugs ................. |vim-game-snake-bugs| 12 | Contributing ......... |vim-game-snake-contributing| 13 | 14 | ============================================================================== 15 | How to start *:VimGameSnake* *vim-game-snake-start* 16 | 17 | " game start 18 | :VimGameSnake 19 | 20 | ============================================================================== 21 | How to play *vim-game-snake-play* 22 | 23 | key | Details 24 | ----|------------ 25 | h | ← 26 | j | ↓ 27 | k | ↑ 28 | l | → 29 | c | end game 30 | q | quit game 31 | 32 | ============================================================================== 33 | License *vim-game-snake-license* 34 | 35 | MIT License 36 | 37 | Copyright (c) 2017 이종립 aka John Grib 38 | 39 | Permission is hereby granted, free of charge, to any person obtaining a copy 40 | of this software and associated documentation files (the "Software"), to deal 41 | in the Software without restriction, including without limitation the rights 42 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 43 | copies of the Software, and to permit persons to whom the Software is 44 | furnished to do so, subject to the following conditions: 45 | 46 | The above copyright notice and this permission notice shall be included in all 47 | copies or substantial portions of the Software. 48 | 49 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 50 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 51 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 52 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 53 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 54 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 55 | SOFTWARE. 56 | 57 | ============================================================================== 58 | bugs *vim-game-snake-bugs* 59 | 60 | https://github.com/johngrib/vim-game-snake/issues 61 | 62 | ============================================================================== 63 | Contributing *vim-game-snake-contributing* 64 | 65 | If you experience any bugs or have feature requests, please open an issue on 66 | GitHub. Fork the source repository on GitHub and send a pull request if you 67 | have any code improvements. 68 | 69 | Author: johngrib(이종립) 70 | repository: https://github.com/johngrib/vim-game-snake 71 | 72 | vim:tw=78:sw=2:ts=2:ft=help:norl:nowrap: 73 | -------------------------------------------------------------------------------- /plugin/vim-game-snake.vim: -------------------------------------------------------------------------------- 1 | command! VimGameSnake :call s:main() 2 | 3 | let g:VimSnakeScore = 0 4 | 5 | let s:config = { 6 | \ 'width': 0, 7 | \ 'height': 0, 8 | \ 'border': 1, 9 | \ 'innerWidth': 0, 10 | \ 'innerHeight': 0, 11 | \ 'limitTop': 0, 12 | \ 'limitBottom': 0, 13 | \ 'limitLeft': 0, 14 | \ 'limitRight': 0, 15 | \ } 16 | 17 | let s:seed = localtime() 18 | 19 | let s:item = { 20 | \ 'wall': 'W', 21 | \ 'body': 'B', 22 | \ 'food': 'F', 23 | \ 'empty': ' ', 24 | \ } 25 | 26 | let s:snake = [ { 'x' : 1 , 'y' : 1 } ] 27 | let s:direction = { 'x': 1, 'y': 0 } 28 | 29 | let s:move = { 30 | \ 'left' : { 'x' : -1 , 'y' : 0 }, 31 | \ 'down' : { 'x' : 0 , 'y' : 1 }, 32 | \ 'up' : { 'x' : 0 , 'y' : -1 }, 33 | \ 'right' : { 'x' : 1 , 'y' : 0 }, 34 | \ } 35 | 36 | function! s:main() 37 | 38 | call s:init() 39 | let l:food = s:newFood() 40 | 41 | let l:loop = 1 42 | while l:loop == 1 43 | 44 | let l:input = nr2char(getchar(0)) 45 | 46 | call s:updateDirection(l:input) 47 | let l:food = s:updateSnake(l:food) 48 | 49 | let l:isGameOver = s:checkGameOver(s:snake) 50 | 51 | if l:input == 'c' || l:isGameOver == 1 52 | break 53 | endif 54 | 55 | call s:moveSnake(l:food) 56 | 57 | if l:input == 'q' 58 | let l:loop = 0 59 | bdelete! 60 | endif 61 | 62 | sleep 100ms 63 | redraw 64 | 65 | endwhile 66 | 67 | endfunction 68 | 69 | function! s:checkGameOver(snake) 70 | let l:head = a:snake[0] 71 | 72 | if s:isWall(l:head['x'], l:head['y']) 73 | \ || s:isSnakeBody(l:head['x'], l:head['y']) 74 | return 1 75 | endif 76 | 77 | return 0 78 | endfunction 79 | 80 | function! s:isWall(x, y) 81 | return s:getCharValue(a:x, a:y) == s:item['wall'] 82 | endfunction 83 | 84 | function! s:isSnakeBody(x, y) 85 | return s:getCharValue(a:x, a:y) == s:item['body'] 86 | endfunction 87 | 88 | 89 | " Game initialize 90 | function! s:init() 91 | 92 | call s:createBuffer() 93 | call s:setLocalSetting() 94 | call s:setConfig() 95 | call s:setColor() 96 | call s:drawScreen(s:config, s:item) 97 | call s:setSnake(s:config['width']/2, s:config['height']/2) 98 | 99 | endfunction 100 | 101 | function! s:newFood() 102 | let l:randomX = s:rand(s:config['innerWidth']) + s:config['border'] 103 | let l:randomY = s:rand(s:config['innerHeight']) + s:config['border'] + 1 104 | let g:VimSnakeScore += 1 105 | 106 | for body in s:snake 107 | if body['x'] == l:randomX && body['y'] == l:randomY 108 | let l:f = s:snake[-1] 109 | let l:randomX = l:f['x'] 110 | let l:randomY = l:f['y'] 111 | break 112 | endif 113 | endfor 114 | 115 | call s:drawChar(l:randomX, l:randomY, 'F') 116 | return { 'x': l:randomX, 'y': l:randomY } 117 | endfunction 118 | 119 | function! s:rand(div) 120 | let s:num = system('echo $RANDOM')[0:-2] 121 | return s:num % a:div 122 | endfunction 123 | 124 | function! s:updateDirection(input) 125 | if a:input == 'h' 126 | if s:direction != s:move['right'] 127 | let s:direction = s:move['left'] 128 | endif 129 | elseif a:input == 'j' 130 | if s:direction != s:move['up'] 131 | let s:direction = s:move['down'] 132 | endif 133 | elseif a:input == 'k' 134 | if s:direction != s:move['down'] 135 | let s:direction = s:move['up'] 136 | endif 137 | elseif a:input == 'l' 138 | if s:direction != s:move['left'] 139 | let s:direction = s:move['right'] 140 | endif 141 | endif 142 | endfunction 143 | 144 | function! s:moveSnake(food) 145 | let l:head = s:snake[0] 146 | let l:tail = s:snake[-1] 147 | call s:drawChar(l:head['x'], l:head['y'], s:item['body']) 148 | call s:drawChar(l:tail['x'], l:tail['y'], s:item['empty']) 149 | call s:drawChar(a:food['x'], a:food['y'], s:item['food']) 150 | endfunction 151 | 152 | function! s:drawChar(x, y, char) 153 | execute "normal! " . a:y . 'gg0' . a:x . 'lr' . a:char . 'gg0' 154 | endfunction 155 | 156 | function! s:updateSnake(food) 157 | let l:dx = s:direction['x'] 158 | let l:dy = s:direction['y'] 159 | let l:head = s:snake[0] 160 | let l:newHead = { 'x': l:head['x'] + l:dx, 'y': l:head['y'] + l:dy } 161 | let s:snake = [ l:newHead ] + s:snake[0:-2] 162 | 163 | if l:newHead['x'] == a:food['x'] && l:newHead['y'] == a:food['y'] 164 | let s:snake = s:snake + [s:snake[-1]] 165 | return s:newFood() 166 | endif 167 | 168 | return a:food 169 | endfunction 170 | 171 | function! s:setSnake(x, y) 172 | let s:snake = [ { 'x': a:x, 'y': a:y }, { 'x': a:x, 'y': a:y } ] 173 | let s:snake = s:snake + s:snake + s:snake 174 | endfunction 175 | 176 | function! s:createBuffer() 177 | silent edit `='VIM-GAME-SNAKE'` 178 | endfunction 179 | 180 | function! s:setLocalSetting() 181 | setlocal bufhidden=wipe 182 | setlocal buftype=nofile 183 | setlocal buftype=nowrite 184 | setlocal nocursorcolumn 185 | setlocal nocursorline 186 | setlocal nolist 187 | setlocal nonumber 188 | setlocal noswapfile 189 | setlocal nowrap 190 | setlocal nonumber 191 | setlocal norelativenumber 192 | endfunction 193 | 194 | function! s:setConfig() 195 | let l:width = winwidth(0) 196 | let l:height = winheight(0) 197 | let l:border = s:config['border'] 198 | let s:config['width'] = l:width 199 | let s:config['height'] = l:height 200 | let s:config['innerWidth'] = l:width - (l:border * 2) 201 | let s:config['innerHeight'] = l:height - (l:border * 2) 202 | let s:config['limitTop'] = l:border 203 | let s:config['limitBottom'] = l:height - l:border 204 | let s:config['limitLeft'] = l:border 205 | let s:config['limitRight'] = l:width - l:border 206 | endfunction 207 | 208 | function! s:drawScreen(config, item) 209 | let l:width = a:config['width'] 210 | let l:height = a:config['height'] 211 | let l:wall = a:item['wall'] 212 | 213 | let l:border = a:config['border'] 214 | let l:innerHeight = a:config['innerHeight'] 215 | let l:innerWidth = a:config['innerWidth'] 216 | 217 | " Draw full screen 218 | let lines = repeat([repeat(l:wall, l:width)], l:height) 219 | 220 | " draw game board 221 | for row in range(1,l:innerHeight) 222 | let lines[row] = repeat(l:wall, l:border) 223 | \ .repeat(' ', l:innerWidth) 224 | \ .repeat(l:wall, l:border) 225 | endfor 226 | 227 | " Draw on buffer 228 | call setline(1, lines) 229 | redraw 230 | endfunction 231 | 232 | function! s:setColor() 233 | syntax match wall 'W' 234 | syntax match snakeHEAD 'H' 235 | syntax match snakeBody 'B' 236 | syntax match snakeFood 'F' 237 | highlight wall ctermfg=blue ctermbg=blue guifg=blue guibg=blue 238 | highlight snakeBody ctermfg=green ctermbg=green guifg=green guibg=green 239 | highlight snakeFood ctermfg=red ctermbg=red guifg=red guibg=red 240 | endfunction 241 | 242 | function! s:getCharValue(x, y) 243 | return getline(a:y)[a:x] 244 | endfunction 245 | 246 | --------------------------------------------------------------------------------