snake.vim 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. " Snake Game that makes vim even more closer to emacs. All vim needs is to be
  2. " able to be a tiling window manager.
  3. command! Snake :call s:snakemain()
  4. let g:VimSnakeScore = 0
  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. let s:seed = localtime()
  17. let s:item = {
  18. \ 'wall': 'W',
  19. \ 'body': 'B',
  20. \ 'food': 'F',
  21. \ 'empty': ' ',
  22. \ }
  23. let s:snake = [ { 'x' : 1 , 'y' : 1 } ]
  24. let s:direction = { 'x': 1, 'y': 0 }
  25. let s:move = {
  26. \ 'left' : { 'x' : -1 , 'y' : 0 },
  27. \ 'down' : { 'x' : 0 , 'y' : 1 },
  28. \ 'up' : { 'x' : 0 , 'y' : -1 },
  29. \ 'right' : { 'x' : 1 , 'y' : 0 },
  30. \ }
  31. function! s:snakemain()
  32. call s:init()
  33. let l:food = s:newFood()
  34. let l:loop = 1
  35. while l:loop == 1
  36. let l:input = nr2char(getchar(0))
  37. call s:updateDirection(l:input)
  38. let l:food = s:updateSnake(l:food)
  39. let l:isGameOver = s:checkGameOver(s:snake)
  40. if l:input == 'c' || l:isGameOver == 1
  41. break
  42. endif
  43. call s:moveSnake(l:food)
  44. if l:input == 'q'
  45. let l:loop = 0
  46. bdelete!
  47. endif
  48. sleep 100ms
  49. redraw
  50. endwhile
  51. endfunction
  52. function! s:checkGameOver(snake)
  53. let l:head = a:snake[0]
  54. if s:isWall(l:head['x'], l:head['y'])
  55. \ || s:isSnakeBody(l:head['x'], l:head['y'])
  56. return 1
  57. endif
  58. return 0
  59. endfunction
  60. function! s:isWall(x, y)
  61. return s:getCharValue(a:x, a:y) == s:item['wall']
  62. endfunction
  63. function! s:isSnakeBody(x, y)
  64. return s:getCharValue(a:x, a:y) == s:item['body']
  65. endfunction
  66. " Game initialize
  67. function! s:init()
  68. call s:createBuffer()
  69. call s:setLocalSetting()
  70. call s:setConfig()
  71. call s:setColor()
  72. call s:drawScreen(s:config, s:item)
  73. call s:setSnake(s:config['width']/2, s:config['height']/2)
  74. endfunction
  75. function! s:newFood()
  76. let l:randomX = s:rand(s:config['innerWidth']) + s:config['border']
  77. let l:randomY = s:rand(s:config['innerHeight']) + s:config['border'] + 1
  78. let g:VimSnakeScore += 1
  79. for body in s:snake
  80. if body['x'] == l:randomX && body['y'] == l:randomY
  81. let l:f = s:snake[-1]
  82. let l:randomX = l:f['x']
  83. let l:randomY = l:f['y']
  84. break
  85. endif
  86. endfor
  87. call s:drawChar(l:randomX, l:randomY, 'F')
  88. return { 'x': l:randomX, 'y': l:randomY }
  89. endfunction
  90. function! s:rand(div)
  91. let s:num = system('echo $RANDOM')[0:-2]
  92. return s:num % a:div
  93. endfunction
  94. function! s:updateDirection(input)
  95. if a:input == 'h'
  96. if s:direction != s:move['right']
  97. let s:direction = s:move['left']
  98. endif
  99. elseif a:input == 'j'
  100. if s:direction != s:move['up']
  101. let s:direction = s:move['down']
  102. endif
  103. elseif a:input == 'k'
  104. if s:direction != s:move['down']
  105. let s:direction = s:move['up']
  106. endif
  107. elseif a:input == 'l'
  108. if s:direction != s:move['left']
  109. let s:direction = s:move['right']
  110. endif
  111. endif
  112. endfunction
  113. function! s:moveSnake(food)
  114. let l:head = s:snake[0]
  115. let l:tail = s:snake[-1]
  116. call s:drawChar(l:head['x'], l:head['y'], s:item['body'])
  117. call s:drawChar(l:tail['x'], l:tail['y'], s:item['empty'])
  118. call s:drawChar(a:food['x'], a:food['y'], s:item['food'])
  119. endfunction
  120. function! s:drawChar(x, y, char)
  121. execute "normal! " . a:y . 'gg0' . a:x . 'lr' . a:char . 'gg0'
  122. endfunction
  123. function! s:updateSnake(food)
  124. let l:dx = s:direction['x']
  125. let l:dy = s:direction['y']
  126. let l:head = s:snake[0]
  127. let l:newHead = { 'x': l:head['x'] + l:dx, 'y': l:head['y'] + l:dy }
  128. let s:snake = [ l:newHead ] + s:snake[0:-2]
  129. if l:newHead['x'] == a:food['x'] && l:newHead['y'] == a:food['y']
  130. let s:snake = s:snake + [s:snake[-1]]
  131. return s:newFood()
  132. endif
  133. return a:food
  134. endfunction
  135. function! s:setSnake(x, y)
  136. let s:snake = [ { 'x': a:x, 'y': a:y }, { 'x': a:x, 'y': a:y } ]
  137. let s:snake = s:snake + s:snake + s:snake
  138. endfunction
  139. function! s:createBuffer()
  140. silent edit `='VIM-GAME-SNAKE'`
  141. endfunction
  142. function! s:setLocalSetting()
  143. setlocal bufhidden=wipe
  144. setlocal buftype=nofile
  145. setlocal buftype=nowrite
  146. setlocal nocursorcolumn
  147. setlocal nocursorline
  148. setlocal nolist
  149. setlocal nonumber
  150. setlocal noswapfile
  151. setlocal nowrap
  152. setlocal nonumber
  153. setlocal norelativenumber
  154. endfunction
  155. function! s:setConfig()
  156. let l:width = winwidth(0)
  157. let l:height = winheight(0)
  158. let l:border = s:config['border']
  159. let s:config['width'] = l:width
  160. let s:config['height'] = l:height
  161. let s:config['innerWidth'] = l:width - (l:border * 2)
  162. let s:config['innerHeight'] = l:height - (l:border * 2)
  163. let s:config['limitTop'] = l:border
  164. let s:config['limitBottom'] = l:height - l:border
  165. let s:config['limitLeft'] = l:border
  166. let s:config['limitRight'] = l:width - l:border
  167. endfunction
  168. function! s:drawScreen(config, item)
  169. let l:width = a:config['width']
  170. let l:height = a:config['height']
  171. let l:wall = a:item['wall']
  172. let l:border = a:config['border']
  173. let l:innerHeight = a:config['innerHeight']
  174. let l:innerWidth = a:config['innerWidth']
  175. " Draw full screen
  176. let lines = repeat([repeat(l:wall, l:width)], l:height)
  177. " draw game board
  178. for row in range(1,l:innerHeight)
  179. let lines[row] = repeat(l:wall, l:border)
  180. \ .repeat(' ', l:innerWidth)
  181. \ .repeat(l:wall, l:border)
  182. endfor
  183. " Draw on buffer
  184. call setline(1, lines)
  185. redraw
  186. endfunction
  187. function! s:setColor()
  188. syntax match wall 'W'
  189. syntax match snakeHEAD 'H'
  190. syntax match snakeBody 'B'
  191. syntax match snakeFood 'F'
  192. highlight wall ctermfg=blue ctermbg=blue guifg=blue guibg=blue
  193. highlight snakeBody ctermfg=green ctermbg=green guifg=green guibg=green
  194. highlight snakeFood ctermfg=red ctermbg=red guifg=red guibg=red
  195. endfunction
  196. function! s:getCharValue(x, y)
  197. return getline(a:y)[a:x]
  198. endfunction