-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
2,037 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
.jukit/ | ||
mylichesstoken.py | ||
fen_test_cases.py | ||
log/ | ||
__pycache__ | ||
.debug_level |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,231 @@ | ||
# vim-lichess | ||
Play lichess in (neo)vim! | ||
|
||
Play online chess in (Neo)Vim! | ||
|
||
![vimlichessdemo](https://user-images.githubusercontent.com/57172028/190704946-4708be17-83c0-4652-ae3e-9cb958faa557.gif) | ||
|
||
### Why? | ||
|
||
Because why not. Not having to leave (Neo)Vim to play online chess should be a basic human right. | ||
|
||
### Honestly, why? | ||
|
||
Honestly, because why not. | ||
|
||
## Requirements | ||
|
||
* (Neo)Vim with python3 support | ||
* `berserk` package for python3 (install via `pip install berserk`) | ||
* A lichess account | ||
|
||
## Basic setup and how to play | ||
|
||
Install the plugin using e.g. `Plug 'luk400/vim-lichess'` in case you're using vim-plug. | ||
|
||
Open (Neo)Vim and run `:LichessFindGame`. You'll be prompted with instructions on how to create and specify your lichess API token in your vim config if you haven't yet (see first variable in section [Other parameters](#other-parameters)). If you've already set your API Token, a new buffer will open and a new game will be started. | ||
|
||
***You can then play by simply left-clicking on a piece and then right-clicking on its destination square***, or alternatively by typing the move in UCI format ([see this link for examples](https://en.wikipedia.org/wiki/Universal_Chess_Interface#Design)) after using the `LichessMakeMoveUCI` command. | ||
|
||
For other actions, such as resigning, offering draws, takebacks, chatting, etc. see section [Commands and mappings](#commands-and-mappings) | ||
|
||
When the game is over, you can either start a new game again using `:LichessFindGame` or delete the buffer using e.g. `:bd` and get back to whatever you've been doing before. | ||
|
||
## Commands and mappings | ||
|
||
#### Commands | ||
* `:LichessFindGame`: Find a new game using [the parameters specified in your vim config](#game-parameters) | ||
* `:LichessResign`: Resign a game | ||
* `:LichessAbort`: Abort a game | ||
* `:LichessClaimVictory`: Claim victory if opponent has abandoned the game (unfortunately there's no way to determine whether a game is "claimable" through lichess API, thus you'll just have to try by running the command when you think the opponent might've abandoned the game) | ||
* `:LichessDrawDecline`: Create or accept a draw offer | ||
* `:LichessDrawOfferAccept`: Decline a draw offer | ||
* `:LichessTakebackOfferAccept`: Create or accept a takeback offer | ||
* `:LichessTakebackOfferDecline`: Decline a takeback offer | ||
* `:LichessMakeMoveUCI`: type a move to make in UCI format ([see this link for examples](https://en.wikipedia.org/wiki/Universal_Chess_Interface#Design)) | ||
* `:LichessChat`: write a message in chat (note that your messages won't register if you're shadowbanned) | ||
|
||
#### Mappings | ||
```vim | ||
nnoremap <buffer> <leader>lm :LichessMakeMoveUCI<cr> | ||
nnoremap <buffer> <leader>lc :LichessChat | ||
nnoremap <buffer> <leader>la :LichessAbort<cr> | ||
nnoremap <buffer> <leader>lr :LichessResign<cr> | ||
nnoremap <buffer> <leader>ldo :LichessOfferDraw<cr> | ||
nnoremap <buffer> <leader>lda :LichessAcceptDraw<cr> | ||
nnoremap <buffer> <leader>ldd :LichessDeclineDraw<cr> | ||
nnoremap <leader>ch :call lichess#play#find_game()<cr> | ||
``` | ||
|
||
## Global variables | ||
|
||
#### Game parameters | ||
```vim | ||
let g:lichess_autoqueen = 1 | ||
" whether to automatically promote to queen or not | ||
let g:lichess_time = 10 | ||
" game time in minutes - must be >= 8, since lichess API only allows rapid or classical games | ||
let g:lichess_increment = 0 | ||
" increment in seconds | ||
let g:lichess_rated = 1 | ||
" whether to play rated games (1) or unrated games (0) | ||
let g:lichess_variant = "standard" | ||
" lichess variant to play -> this plugin has currently only been tested with 'standard'! possible values: ['standard', 'chess960', 'crazyhouse', 'antichess', 'atomic', 'horde', 'kingOfTheHill', 'racingKings', 'threeCheck'] | ||
let g:lichess_color = "random" | ||
" which color you want to play as. possible values: ['white', 'black', 'random'] | ||
let g:lichess_rating_range = [] | ||
" rating range of your opponents, can be an empty list to use the default (recommended) or a list like `[low,high]`, where `low` and `high` are integers. | ||
``` | ||
|
||
#### Other parameters | ||
```vim | ||
let g:lichess_api_token = '' | ||
" your required lichess API token. you can easily easily create one which you can put in your config using this link: https://lichess.org/account/oauth/token/create?scopes[]=challenge:write&scopes[]=board:play&description=vim+lichess | ||
let g:python_cmd = 'python3' | ||
" python command to run server in background - this should be the python executable for which berserk is installed (can also be a full path) | ||
let g:lichess_debug_level = -1 | ||
" set debugging level. -1 means nothing is logged and no log files are created, 0 -> all info is logged, 1 -> only warnings and 'worse' are logged, 2 -> only errors and 'worse' are logged, 3 -> only crashes are logged | ||
``` | ||
|
||
#### Highlighting | ||
|
||
In case you want change the board colors or other highlighting options, you can modify any of the following highlights and put them in your vim config (AFTER the plugin is loaded - e.g. after `call plug#end()` in case you're using vim-plug) to overwrite them: | ||
|
||
```vim | ||
highlight lichess_black_squares guibg=#B58863 ctermbg=94 | ||
" highlighting of black squares | ||
highlight lichess_white_squares guibg=#F0D9B5 ctermbg=7 | ||
" highlighting of white squares | ||
highlight lichess_black_pieces guifg=#000000 guibg=#000000 ctermbg=0 ctermfg=0 | ||
" highlighting of black pieces | ||
highlight lichess_white_pieces guifg=#ffffff guibg=#ffffff ctermbg=15 ctermfg=15 | ||
" highlighting of white pieces | ||
highlight lichess_from_square_dark guifg=#AAA23A guibg=#AAA23A ctermbg=172 ctermfg=172 | ||
" highlighting of the previous and new square of the latest moved piece if it's a dark square | ||
highlight lichess_from_square_light guifg=#CDD26A guibg=#CDD26A ctermbg=178 ctermfg=178 | ||
" highlighting of the previous and new square of the latest moved piece if it's a light square | ||
highlight lichess_cell_delimiters guifg=#000000 guibg=#000000 ctermbg=0 ctermfg=0 | ||
" highlighting of vertical cell delimiters between squares | ||
highlight lichess_user_turn guibg=#3eed6c guifg=#000000 ctermbg=2 ctermfg=0 cterm=bold gui=bold | ||
" highlighting of the name of the user whose turn it currently is | ||
highlight lichess_user_noturn guifg=#ffffff ctermfg=15 cterm=bold gui=bold | ||
" highlighting of the name of the user whose turn it's currently not | ||
highlight lichess_searching_game guifg=#42d7f5 guibg=#000000 ctermfg=14 ctermbg=0 cterm=bold gui=bold | ||
" highlighting of 'searching game...' prompt | ||
highlight lichess_game_ended guibg=#e63c30 guifg=#ffffff ctermbg=1 ctermfg=15 cterm=bold gui=bold | ||
" highlighting of last game status (e.g. 'MATE' or 'RESIGN') | ||
highlight lichess_chat guibg=#e3f27e guifg=#000000 ctermbg=191 ctermfg=0 | ||
" highlighting of of opponent chat messages | ||
highlight lichess_chat_system guibg=#ed8787 guifg=#000000 ctermbg=178 ctermfg=0 | ||
" highlighting of of lichess chat messages | ||
highlight lichess_chat_you guibg=#b4e364 guifg=#000000 ctermbg=190 ctermfg=0 | ||
" highlighting of your chat messages | ||
highlight lichess_chat_bold guibg=#e3f27e guifg=#000000 ctermbg=191 ctermfg=0 cterm=bold gui=bold | ||
" highlighting of of 'CHAT:' prompt | ||
highlight lichess_move_info guibg=#9ea832 guifg=#000000 ctermbg=3 ctermfg=0 cterm=bold gui=bold | ||
" echohl highlighting of echoed move-message | ||
highlight lichess_too_many_requests guibg=#c20202 guifg=#ffffff ctermbg=15 ctermfg=9 cterm=bold gui=bold | ||
" echohl highlighting of too_many_requests error | ||
``` | ||
|
||
#### Piece representation | ||
|
||
In case you don't like my piece design, you can design your own as shown below. | ||
You can also change their width/height (number of characters in strings/number of strings in list) to make them bigger if you want more detail, as long as you follow the following restrictions: | ||
* all pieces must have the same height (number of strings in piece list) | ||
* all pieces must have the same width (number of characters in the strings) | ||
* there must be exactly one unique non-whitespace character for all black and one unique non-whitespace character for all white pieces. This can not be the same for the white and black pieces and it must have a length of 1 (there are certain characters which have a different length in vim - e.g.: `echo len('║')` will print `3` even though it's a single character) | ||
|
||
|
||
```vim | ||
" black pieces | ||
let g:lichess_piece_p = | ||
\ [" ", | ||
\ " ,, ", | ||
\ " ,,,, ", | ||
\ " ,,,, ", | ||
\ " ,,,, ", | ||
\ " "] " black pawn | ||
let g:lichess_piece_r = | ||
\ [" ", | ||
\ " , ,, , ", | ||
\ " ,,,,,, ", | ||
\ " ,,,,,, ", | ||
\ " ,,,,,,,, ", | ||
\ " "] " black rook | ||
let g:lichess_piece_k = | ||
\ [" ", | ||
\ " ,, ", | ||
\ " ,,,,,,,, ", | ||
\ " ,, ", | ||
\ " ,, ", | ||
\ " "] " black king | ||
let g:lichess_piece_q = | ||
\ [" ", | ||
\ " , ,, , ", | ||
\ " ,,,, ", | ||
\ " ,, ", | ||
\ " ,,,,,, ", | ||
\ " "] " black queen | ||
let g:lichess_piece_b = | ||
\ [" ", | ||
\ " ,,,, ", | ||
\ " ,,,, ", | ||
\ " ,, ", | ||
\ " ,,,,,, ", | ||
\ " "] " black bishop | ||
let g:lichess_piece_n = | ||
\ [" ", | ||
\ " ,,, ", | ||
\ " ,,, ,, ", | ||
\ " ,,, ", | ||
\ " ,,,,,, ", | ||
\ " "] " black knight | ||
" white pieces | ||
let g:lichess_piece_P = | ||
\ [" ", | ||
\ " ;; ", | ||
\ " ;;;; ", | ||
\ " ;;;; ", | ||
\ " ;;;; ", | ||
\ " "] " white pawn | ||
let g:lichess_piece_R = | ||
\ [" ", | ||
\ " ; ;; ; ", | ||
\ " ;;;;;; ", | ||
\ " ;;;;;; ", | ||
\ " ;;;;;;;; ", | ||
\ " "] " white rook | ||
let g:lichess_piece_K = | ||
\ [" ", | ||
\ " ;; ", | ||
\ " ;;;;;;;; ", | ||
\ " ;; ", | ||
\ " ;; ", | ||
\ " "] " white king | ||
let g:lichess_piece_Q = | ||
\ [" ", | ||
\ " ; ;; ; ", | ||
\ " ;;;; ", | ||
\ " ;; ", | ||
\ " ;;;;;; ", | ||
\ " "] " white queen | ||
let g:lichess_piece_B = | ||
\ [" ", | ||
\ " ;;;; ", | ||
\ " ;;;; ", | ||
\ " ;; ", | ||
\ " ;;;;;; ", | ||
\ " "] " white bishop | ||
let g:lichess_piece_N = | ||
\ [" ", | ||
\ " ;;; ", | ||
\ " ;;; ;; ", | ||
\ " ;;; ", | ||
\ " ;;;;;; ", | ||
\ " "] " white knight | ||
``` | ||
|
||
# Credit | ||
|
||
All credit goes to my huge procrastination issues |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
"""""""""""""""""""" | ||
" highlighting setup | ||
"""""""""""""""""""" | ||
let params = lichess#play#get_square_dim_and_piece_chars() | ||
let s:square_width = params[0] | ||
let s:square_height = params[1] | ||
let s:white_piece_char = params[2] | ||
let s:black_piece_char = params[3] | ||
let s:start_wcell = '`' | ||
let s:start_bcell = "'" | ||
let s:move_cell_dark = '-' | ||
let s:move_cell_light = '_' | ||
|
||
if !hlexists('lichess_cell_delimiters') | ||
highlight lichess_cell_delimiters guifg=#000000 guibg=#000000 ctermbg=0 ctermfg=0 | ||
endif | ||
if !hlexists('lichess_black_squares') | ||
highlight lichess_black_squares guibg=#B58863 ctermbg=94 | ||
endif | ||
if !hlexists('lichess_white_squares') | ||
highlight lichess_white_squares guibg=#F0D9B5 ctermbg=7 | ||
endif | ||
if !hlexists('lichess_black_pieces') | ||
highlight lichess_black_pieces guifg=#000000 guibg=#000000 ctermbg=0 ctermfg=0 | ||
endif | ||
if !hlexists('lichess_white_pieces') | ||
highlight lichess_white_pieces guifg=#ffffff guibg=#ffffff ctermbg=15 ctermfg=15 | ||
endif | ||
if !hlexists('lichess_from_square_dark') | ||
highlight lichess_from_square_dark guifg=#AAA23A guibg=#AAA23A ctermbg=172 ctermfg=172 | ||
endif | ||
if !hlexists('lichess_from_square_light') | ||
highlight lichess_from_square_light guifg=#CDD26A guibg=#CDD26A ctermbg=178 ctermfg=178 | ||
endif | ||
|
||
let s:empty_line = repeat(' ', s:square_width) | ||
let s:empty_line_move_dark = repeat(s:move_cell_dark, s:square_width) | ||
let s:empty_line_move_light = repeat(s:move_cell_light, s:square_width) | ||
|
||
fun! lichess#board_setup#syntax_matching() abort | ||
syn clear | ||
|
||
exe 'syn match lichess_cell_delimiters /' . s:start_wcell . '/ contains=ALL containedin=ALL' | ||
exe 'syn match lichess_cell_delimiters /' . s:start_bcell . '/ contains=ALL containedin=ALL' | ||
|
||
exe 'syn match lichess_black_squares /' . s:start_bcell . '.\{-}' . s:start_wcell . '/ containedin=ALL' | ||
exe 'syn match lichess_white_squares /' . s:start_wcell . '.\{-}' . s:start_bcell . '/ containedin=ALL' | ||
|
||
exe 'syn match lichess_black_pieces /' . s:black_piece_char . '/ containedin=lichess_black_squares,lichess_white_squares' | ||
exe 'syn match lichess_white_pieces /' . s:white_piece_char . '/ containedin=lichess_black_squares,lichess_white_squares' | ||
|
||
exe 'syn match lichess_from_square_dark /' . s:move_cell_dark . '/ containedin=lichess_black_squares,lichess_white_squares' | ||
exe 'syn match lichess_from_square_light /' . s:move_cell_light . '/ containedin=lichess_black_squares,lichess_white_squares' | ||
endfun | ||
|
||
|
||
"""""""""""""""" | ||
" board creation | ||
"""""""""""""""" | ||
let s:piece_symbols = { | ||
\ 'p': p, | ||
\ 'r': r, | ||
\ 'k': k, | ||
\ 'q': q, | ||
\ 'b': b, | ||
\ 'n': n, | ||
\ 'P': P, | ||
\ 'R': R, | ||
\ 'K': K, | ||
\ 'Q': Q, | ||
\ 'B': B, | ||
\ 'N': N, | ||
\ } | ||
|
||
function! s:create_board(fen, latest_move) abort | ||
" example FEN: | ||
" rn2k1r1/ppp1pp1p/3p2p1/5bn1/P7/2N2B2/1PPPPP2/2BNK1RR | ||
|
||
if a:latest_move != "None" | ||
let from_row = a:latest_move[0] - 1 | ||
let from_column = a:latest_move[1] - 1 | ||
let to_row = a:latest_move[2] - 1 | ||
let to_column = a:latest_move[3] - 1 | ||
else | ||
let from_row = -1 | ||
let from_column = -1 | ||
let to_row = -1 | ||
let to_column = -1 | ||
endif | ||
|
||
let board = repeat(s:start_wcell, 9 + s:square_width * 8) . "\n" | ||
let i = 0 | ||
for str in split(a:fen, '/') | ||
" rn2k1r1 | ||
for j in range(s:square_height) | ||
let n = 0 | ||
for char in str | ||
" r | ||
let next_cell_black = fmod(i + n, 2) == 0.0 | ||
let is_move_cell = (i == from_row) && (n == from_column) || (i == to_row) && (n == to_column) | ||
if next_cell_black | ||
let board = board . s:start_wcell | ||
else | ||
let board = board . s:start_bcell | ||
endif | ||
|
||
if str2float(char) > 0 | ||
for k in range(char) | ||
if is_move_cell && !next_cell_black | ||
let board = board . s:empty_line_move_dark | ||
elseif is_move_cell | ||
let board = board . s:empty_line_move_light | ||
else | ||
let board = board . s:empty_line | ||
endif | ||
|
||
if next_cell_black && (k < char - 1) | ||
let board = board . s:start_bcell | ||
elseif (k < char - 1) | ||
let board = board . s:start_wcell | ||
endif | ||
let n += 1 | ||
let next_cell_black = fmod(i + n, 2) == 0.0 | ||
let is_move_cell = (i == from_row) && (n == from_column) || (i == to_row) && (n == to_column) | ||
endfor | ||
else | ||
if is_move_cell && !next_cell_black | ||
let board = board . substitute(s:piece_symbols[char][j], ' ', s:move_cell_dark, 'g') | ||
elseif is_move_cell | ||
let board = board . substitute(s:piece_symbols[char][j], ' ', s:move_cell_light, 'g') | ||
else | ||
let board = board . s:piece_symbols[char][j] | ||
endif | ||
let n += 1 | ||
endif | ||
endfor | ||
|
||
if fmod(i, 2) == 0.0 | ||
let board = board . s:start_wcell . "\n" | ||
else | ||
let board = board . s:start_bcell . "\n" | ||
endif | ||
endfor | ||
let i += 1 | ||
endfor | ||
let board = board . repeat(s:start_wcell, 9 + s:square_width * 8) | ||
|
||
return board | ||
endfunction | ||
|
||
|
||
function! lichess#board_setup#display_board(fen, latest_move) abort | ||
let board = s:create_board(a:fen, a:latest_move) | ||
call append(0, split(board, '\n')) | ||
endfunction |
Oops, something went wrong.