This post was originally written on Codeforces; relevant discussion can be found here.
Since someone recently asked me about my competitive programming setup, and I like tinkering with my setup to make it as useful and minimal as possible, I thought I should share my setup that I’ve used for the past few years and a modified version that I’ve used at onsite ICPC contests. I’ve also talked to a few people who went to IOI and had a similar setup, and I’m fairly confident that at least some people will be able to successfully use this without having to worry too much about the details, like I did. This is definitely NOT the only way to set up a basic environment, but it was the way that worked for me for quite a long time.
This post is also meant as a companion post to this post on using the command line.
Before we start with the tools, it’s a good point to mention this resource as another useful reference for installing vim and g++.
About the choice of tools
The choice of tools here is motivated by two things — availability and minimalism.
About availability: most onsite contests have a Linux system (which is
almost always Ubuntu), and while installing the most common compiler
used for competitive programming (g++) on Ubuntu, the meta-package
build-essentials
is required by the standard installation procedure.
This package also installs make
. Most onsite contests provide gdb
too. As far as vim
is concerned, it is provided on almost all onsite
contests as well (and if it is not present, you can use vi
as a good
enough substitute).
Needless to say, this setup assumes that you have a Linux machine or Linux VM. If you’re on Windows, using WSL should suffice. Most of these things should also work on MacOS (speaking from my limited experience of testing an ICPC contest while stuck with a machine with MacOS on it).
The fact that they’re minimalist also correlates with the fact that
they’re fast and easy to set up (sometimes they don’t need anything to
be set up either) and won’t take a ton of your time looking at
complicated menus and waiting for startup. Note that using vim
might
be a bit too much if you haven’t used it earlier and you’re not willing
to devote a few days to getting comfortable with it, so it’s a matter of
personal preference to use some other text editor like Sublime or VS
Code. However, I do not know of any alternative to make
(other than
writing a shell script, which is a bit inconvenient unless you’re a
sh/bash expert) and gdb
that are as widely available. I’d like to
re-emphasize the fact that this is just my setup; your mileage might
vary.
Now about the tools and how to install them:
vim
is a minimalist and customizable text editor that allows you to do a lot of stuff in a no-nonsense way. Its keybindings can be a bit counterintuitive at first, but once you get used to it, it makes writing and editing code very convenient. Installation instructions can be found at the link I shared earlier.make
is a minimal build system that reads something called a Makefile and figures out how to do your compilation for you. Installation instructions can be found at the link I shared earlier, under the heading “An example of a workflow in Vim”.gdb
is a command-line debugger that works for a lot of languages and is pretty convenient and intuitive to use (and also quite scalable). For installation instructions, a search on your favourite search engine should be sufficient.
Setup for onsite contests
This is a more concise setup that I’ve used for onsite contests, and people might want to build upon it further.
I prefer using the following template, but for ICPC, I added some things from the KACTL template to keep things uniform in the codebook.
Template
#include <bits/stdc++.h>
using namespace std;
using ll = int64_t;
int main() {
cin.tie(nullptr)->sync_with_stdio(false);
}
Vim
For setting up vim, you just need to create a file ~/.vimrc
which
stores all your configurations.
A concise vimrc
set nocp
filetype plugin indent on
syntax enable
autocmd BufNewFile,BufRead * setlocal formatoptions-=cro
set hi=500 wmnu ru nu rnu bs=eol,start,indent whichwrap+=<,>,h,l hls is lz noeb t_vb= tm=500 enc=utf8 ffs=unix nowb noswf et sta lbr tw=200 ai si wrap vi= ls=2 cb=unnamedplus,unnamed sw=4 ts=4
colorscheme slate
let mapleader = ","
inoremap {<CR> {<CR><BS>}<Esc>O
inoremap [<CR> [<CR><BS>]<Esc>O
inoremap (<CR> (<CR><BS>)<Esc>O
map 0 ^
packadd termdebug
let g:termdebug_popup=0
let g:termdebug_wide=163
let &t_ut=''
A bit more beginner-friendly vimrc
set nocp
filetype plugin indent on
syntax enable
autocmd BufNewFile,BufRead * setlocal formatoptions-=cro
source $VIMRUNTIME/delmenu.vim
source $VIMRUNTIME/menu.vim
set hi=500 wmnu ru nu rnu bs=eol,start,indent whichwrap+=<,>,h,l hls is lz noeb t_vb= tm=500 enc=utf8 ffs=unix nowb noswf et sta lbr tw=200 ai si wrap vi= ls=2 cb=unnamedplus,unnamed mouse=a mousem=popup sw=4 ts=4
colorscheme slate
let mapleader = ","
inoremap {<CR> {<CR><BS>}<Esc>O
inoremap [<CR> [<CR><BS>]<Esc>O
inoremap (<CR> (<CR><BS>)<Esc>O
map 0 ^
" :Termdebug <executable_name>, :help terminal-debug, Ctrl+W twice to switch windows
packadd termdebug
let g:termdebug_popup=0
let g:termdebug_wide=163
let &t_ut=''
let &t_SI="\<Esc>[6 q"
let &t_SR="\<Esc>[4 q"
let &t_EI="\<Esc>[2 q"
Explanation of what everything does
set nocp
: required for some technical issues, most prominently when you load a vimrc withvim -u <vimrc_path>
or to avoid bugs when you just usevi
instead. Basically, always keep it in your vimrc.filetype plugin indent on
: turns on filetype detection, plugins and indentation. If you’re more curious about what this does, read this.syntax enable
: turns on syntax highlighting.autocmd BufNewFile,BufRead * setlocal formatoptions-=cro
: turns off some comment continuation behavior (when a comment continues to a new line or is broken across lines) that I don’t like. You can consider removing this line if you’re fine with it.source $VIMRUNTIME/delmenu.vim
andsource $VIMRUNTIME/menu.vim
: adds support for menus, if you still want to use some kinds of menus. For menus to get triggered with a right click, you can setmouse=a mousem=popup
like I did in the beginner vimrc.hi=500 wmnu ru nu rnu bs=eol,start,indent whichwrap+=<,>,h,l hls is lz noeb t_vb
tm=500 enc=utf8 ffs=unix nowb noswf et sta lbr tw=200 ai si wrap vi= ls=2 cb=unnamedplus,unnamed mouse=a mousem=popup sw=4 ts=4=: does the following things in order: sets history to 500 lines, makes the numbering more useful, sets some wrapping options, sets some searching and match-highlighting options, turns off audio and visual notifications for errors, manages encoding and filesystem related issues, turns off backups etc., handles indenting and converts all tabs to spaces, manages clipboardscolorscheme slate
: sets the colorscheme to slate. There are a few inbuilt colorschemes, and I just happened to find it to be convenient for long hours of coding.let mapleader = ","
: sets the leader key to a comma. Not really important if you don’t use it.inoremap {<CR> {<CR><BS>}<Esc>O
etc: when you input a curly brace and press return (fast enough), it autocompletes the ending curly brace. Similarly for square brackets and parentheses.map 0 ^
: when you input0
in normal mode, it’ll go to the first non-whitespace character.packadd termdebug
,let g:termdebug_popup=0
andlet g:termdebug_wide=163
: adds a package to run gdb from inside vim and sets up a few window options. The documentation for it can be found using the thing mentioned in the comment before it.let &t_ut
’’=: fixes some background-color related issues that I find annoying, but it’s barely noticeable.let &t_SI
"\<Esc>[6 q", =let &t_SR
"\<Esc>[4 q"= andlet &t_EI
"\<Esc>[2 q"=: sets the cursor shapes when you’re in insert mode, normal mode and replace mode.
Makefile
Note that you can run make
without any setup at all. For example, if
the path (relative or absolute) to your cpp file is
<some_path>/code.cpp
, you can run make <some_path>/code
and it will
generate an executable at the path <some_path>/code
, using the default
compilation flags. make
also prints the command that it used to
compile the file.
One feature of make
is that once you compile a file, unless there are
changes in the source file, it won’t compile it again. To counter that,
either use make -B
instead of make
, or use the Makefile below and
run make clean
which removes all executables from the current
directory upto max depth 1.
For using more compilation flags with make
, create a file named
Makefile
and store the following Makefile in it (you can modify this
too).
Makefile
D ?= 0
ifeq ($(D), 1)
CXXFLAGS=-std=c++17 -g -Wall -Wextra -Wpedantic -Wshadow -Wformat=2 -Wfloat-equal -Wconversion -Wlogical-op -Wshift-overflow=2 -Wduplicated-cond -Wcast-qual -Wcast-align -Wno-variadic-macros -D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC -fsanitize=address -fsanitize=undefined -fno-sanitize-recover -fstack-protector -fsanitize-address-use-after-scope
else
CXXFLAGS=-O2 -std=c++17
endif
clean:
find . -maxdepth 1 -type f -executable -delete
Explanation
CXXFLAGS
is a variable that refers to the flags used to compile a C++ file.D
is a variable that we defined, and it is equal to0
by default. If instead of runningmake a
, we runmake a D=1
, it will use the flags that are in the first line. These flags make your program slower, but make it easier to debug your program using gdb and have some assertion checks that can sometimes tell you the issue when the program runs without having to use gdb.
GDB
GDB doesn’t really need setup. With the above flags, the most I have ever needed was to run the executable with debug flags in gdb, and look at the backtrace to pinpoint the line with the error. To do that, I do the following (you can also do this in vim using the termdebug package that is added in the vimrc above and is provided by vim out of the box):
- Run
gdb <executable_name>
- If you have an input file called
in.txt
, typerun < in.txt
and hit enter. If you don’t have an input file, you can just give it input like in a terminal. Output redirection works as well. - If the program has errors, it will stop at some point with an error.
To look at the backtrace, type
bt
and hit enter.
Setup for online contests
My setup for online contests is a bit more involved, and I’ll keep this section a bit more concise, since you can just set this up once and never have to worry about it again, in contrast with onsite contests, where every character counts.
I use a bigger template for online contests, which is as follows:
Template
#ifndef LOCAL
#pragma GCC optimize("O3,unroll-loops")
#pragma GCC target("avx2,bmi,bmi2,popcnt,lzcnt")
// #pragma GCC target("sse4.2,bmi,bmi2,popcnt,lzcnt")
#endif
#include "bits/stdc++.h"
#ifdef DEBUG
#include "includes/debug/debug.hpp"
#else
#define debug(...) 0
#endif
using ll = int64_t;
using ull = uint64_t;
using ld = long double;
using namespace std;
int main() {
cin.tie(nullptr)->sync_with_stdio(false);
// cout << setprecision(20) << fixed;
int _tests = 1;
// cin >> _tests;
for (int _test = 1; _test <= _tests; ++_test) {
// cout << "Case #" << _test << ": ";
}
}
Note that there’s an include for debugging, and it’s pretty easy to use (I’ve mentioned it here).
Vim
Note that the following vimrc has some non-competitive-programming stuff
as well, since I use vim as my primary editor. For more information on
each plugin (each such line starts with Plug
or Plugin
), you can
search for it on GitHub. For instance, for the line
Plug 'sbdchd/neoformat'
, the relevant GitHub repo is
this. I use the snippet manager
UltiSnips and clang-format and clangd for code formatting and static
analysis purposes. I used to use some of the commented plugins earlier,
so those can be useful too.
vimrc
filetype plugin indent on " required
syntax enable
" hack to enable python3 rather than python2
if has('python3')
endif
" Plugins
call plug#begin('~/.vim/plugged')
Plug 'sbdchd/neoformat'
let g:opambin = substitute(system('opam config var bin'),'\n$','','''')
let g:neoformat_ocaml_ocamlformat = {
\ 'exe': g:opambin . '/ocamlformat',
\ 'no_append': 1,
\ 'stdin': 1,
\ 'args': ['--enable-outside-detected-project', '--name', '"%:p"', '-']
\ }
let g:neoformat_enabled_ocaml = ['ocamlformat']
let g:neoformat_python_autopep8 = {
\ 'exe': 'autopep8',
\ 'args': ['-s 4', '-E'],
\ 'replace': 1,
\ 'stdin': 1,
\ 'env': ["DEBUG=1"],
\ 'valid_exit_codes': [0, 23],
\ 'no_append': 1,
\ }
let g:neoformat_enabled_python = ['autopep8']
let g:neoformat_try_formatprg = 1
" Enable alignment
let g:neoformat_basic_format_align = 1
" Enable tab to spaces conversion
let g:neoformat_basic_format_retab = 1
" Enable trimmming of trailing whitespace
let g:neoformat_basic_format_trim = 1
Plug 'lervag/vimtex'
let g:tex_flavor='latex'
let g:vimtex_view_method='zathura'
let g:vimtex_quickfix_mode=0
set conceallevel=2
let g:tex_conceal='abdmg'
Plug 'sirver/ultisnips'
let g:UltiSnipsExpandTrigger = '<tab>'
let g:UltiSnipsJumpForwardTrigger = '<tab>'
let g:UltiSnipsJumpBackwardTrigger = '<s-tab>'
Plug 'neoclide/coc.nvim', {'branch': 'release'}
" also see https://github.com/clangd/coc-clangd for clangd support
" for haskell: https://www.reddit.com/r/vim/comments/k3ar3i/cocvim_haskelllanguageserver_starter_tutorial_2020/
try
nmap <silent> [c :call CocAction('diagnosticNext')<cr>
nmap <silent> ]c :call CocAction('diagnosticPrevious')<cr>
endtry
let g:loaded_sql_completion = 0
let g:omni_sql_no_default_maps = 1
Plug 'junegunn/fzf', { 'dir': '~/.fzf', 'do': './install --all' }
" Plug 'jakobkogler/Algorithm-DataStructures'
Plug 'xuhdev/vim-latex-live-preview', { 'for': 'tex' }
let g:livepreview_previewer = 'zathura'
Plug 'godlygeek/csapprox'
let g:CSApprox_hook_post = [
\ 'highlight Normal ctermbg=NONE',
\ 'highlight LineNr ctermbg=NONE',
\ 'highlight SignifyLineAdd cterm=bold ctermbg=NONE ctermfg=green',
\ 'highlight SignifyLineDelete cterm=bold ctermbg=NONE ctermfg=red',
\ 'highlight SignifyLineChange cterm=bold ctermbg=NONE ctermfg=yellow',
\ 'highlight SignifySignAdd cterm=bold ctermbg=NONE ctermfg=green',
\ 'highlight SignifySignDelete cterm=bold ctermbg=NONE ctermfg=red',
\ 'highlight SignifySignChange cterm=bold ctermbg=NONE ctermfg=yellow',
\ 'highlight SignColumn ctermbg=NONE',
\ 'highlight CursorLine ctermbg=NONE cterm=underline',
\ 'highlight Folded ctermbg=NONE cterm=bold',
\ 'highlight FoldColumn ctermbg=NONE cterm=bold',
\ 'highlight NonText ctermbg=NONE',
\ 'highlight clear LineNr'
\]
Plug 'powerline/powerline'
let g:Powerline_symbols = 'fancy'
set fillchars+=stl:\ ,stlnc:\
set termencoding=utf-8
Plug 'preservim/nerdcommenter'
Plug 'alx741/vim-rustfmt'
call plug#end()
set rtp+=~/.vim/bundle/Vundle.vim
call vundle#begin()
Plugin 'dense-analysis/ale'
Plugin 'morhetz/gruvbox'
Plugin 'Chiel92/vim-autoformat'
Plugin 'VundleVim/Vundle.vim'
Plugin 'octol/vim-cpp-enhanced-highlight'
let g:cpp_member_variable_highlight = 1
let g:cpp_class_decl_highlight = 1
let g:cpp_class_scope_highlight = 1
let g:cpp_posix_standard = 1
" let g:cpp_experimental_simple_template_highlight = 1
" let g:cpp_no_function_highlight = 1
" Plugin 'NavneelSinghal/vim-cpp-auto-include'
" autocmd BufWritePre /path/to/workspace/**.cpp :ruby CppAutoInclude::process
Plugin 'rhysd/vim-clang-format'
Plugin 'iamcco/markdown-preview.nvim'
let g:mkdp_auto_start = 0
let g:mkdp_auto_close = 1
let g:mkdp_refresh_slow = 0
let g:mkdp_command_for_global = 0
let g:mkdp_open_to_the_world = 0
let g:mkdp_open_ip = ''
let g:mkdp_browser = ''
let g:mkdp_echo_preview_url = 0
let g:mkdp_browserfunc = ''
let g:mkdp_preview_options = {
\ 'mkit': {},
\ 'katex': {},
\ 'uml': {},
\ 'maid': {},
\ 'disable_sync_scroll': 0,
\ 'sync_scroll_type': 'middle',
\ 'hide_yaml_meta': 1,
\ 'sequence_diagrams': {},
\ 'flowchart_diagrams': {},
\ 'content_editable': v:false,
\ 'disable_filename': 0
\ }
let g:mkdp_markdown_css = ''
let g:mkdp_highlight_css = ''
let g:mkdp_port = ''
let g:mkdp_page_title = '「${name}」'
let g:mkdp_filetypes = ['markdown']
Plugin 'vim-airline/vim-airline'
Plugin 'vim-airline/vim-airline-themes'
let g:airline_theme='badwolf'
let g:airline#extensions#tabline#enabled=1
let g:airline#extensions#tabline#formatter='unique_tail'
let g:airline_powerline_fonts=1
Plugin 'udalov/kotlin-vim'
" https://github.com/iamcco/markdown-preview.nvim/issues/43
Plugin 'davidhalter/jedi-vim'
call vundle#end() " required
execute pathogen#infect()
" Autocmds
autocmd VimEnter * SyntasticToggleMode " disable syntastic by default
autocmd BufNewFile,BufRead * setlocal formatoptions-=cro
autocmd BufNewFile ~/Desktop/cp/workspace/**.cpp 0r ~/.vim/templates/template.cpp
autocmd BufNewFile,BufRead *.kt set filetype=kotlin
autocmd BufReadPost * if line("'\"") > 1 && line("'\"") <= line("$") | exe "normal! g'\"" | endif
let &t_SI = "\<Esc>[6 q"
let &t_SR = "\<Esc>[4 q"
let &t_EI = "\<Esc>[2 q"
command W w !sudo tee % > /dev/null
" Basic
let $LANG='en'
set langmenu=en
source $VIMRUNTIME/delmenu.vim
source $VIMRUNTIME/menu.vim
let indentvalue=4
set noshowcmd noruler
set history=500
set autoread
set so=7
set wildmenu
set wildignore=*.o,*~,*.pyc,*/.git/*,*/.hg/*,*/.svn/*,*/.DS_Store
set ruler
set number
set relativenumber
set cmdheight=2
set hid
set backspace=eol,start,indent
set whichwrap+=<,>,h,l
set ignorecase
set smartcase
set hlsearch
set incsearch
set lazyredraw
set magic
set showmatch
set mat=2
set noerrorbells
set novisualbell
set t_vb=
set tm=500
set nocursorline
set nocursorcolumn
set foldcolumn=1
set encoding=utf8
set ffs=unix,dos,mac
set nobackup
set nowb
set noswapfile
set expandtab
set smarttab
let &shiftwidth=indentvalue
let &tabstop=indentvalue
set lbr
set tw=200
set autoindent
set smartindent
set wrap
set viminfo=
set bg=dark
set t_Co=256
set switchbuf=useopen,usetab,newtab
set stal=2
set laststatus=2
set clipboard+=unnamedplus
set mouse=a
set mousemodel=popup
set ttimeout
set ttimeoutlen=1
set ttyfast
" Appearance
try
colorscheme gruvbox
let g:gruvbox_transparent_bg=1
let g:gruvbox_contrast_dark='hard'
let g:gruvbox_invert_tabline=1
catch
endtry
if has("gui_running")
set guioptions-=T
set guioptions-=e
set guitablabel=%M\ %t
set guifont=JetBrains\ Mono\ 12
endif
" Keymappings
inoremap {<CR> {<CR><BS>}<Esc>O
inoremap [<CR> [<CR><BS>]<Esc>O
inoremap (<CR> (<CR><BS>)<Esc>O
let mapleader = ","
nmap <leader>w :w!<cr>
map <space> /
map <c-space> ?
map <silent> <leader><cr> :noh<cr>
map <C-j> <C-W>j
map <C-k> <C-W>k
map <C-h> <C-W>h
map <C-l> <C-W>l
map <C-n> :NERDTreeToggle<CR>
map <leader>l :bnext<cr>
map <leader>h :bprevious<cr>
let g:lasttab = 1
au TabLeave * let g:lasttab = tabpagenr()
map 0 ^
" for setting up inkscape-figures
inoremap <C-f> <Esc>: silent exec '.!inkscape-figures create "'.getline('.').'" "'.b:vimtex.root.'/figures/"'<CR><CR>:w<CR>
nnoremap <C-f> : silent exec '!inkscape-figures edit "'.b:vimtex.root.'/figures/" > /dev/null 2>&1 &'<CR><CR>:redraw!<CR>
ca Hash w !cpp -dD -P -fpreprocessed \| tr -d '[:space:]' \
\| md5sum \| cut -c-6
function! CmdLine(str)
call feedkeys(":" . a:str)
endfunction
" debugging stuff: to run in vim, run :Termdebug <executable_name>, and :help terminal-debug for help (use Ctrl+W twice to switch between windows)
packadd termdebug
let g:termdebug_popup = 0
let g:termdebug_wide = 163
set rtp^="~/.opam/default/share/ocp-indent/vim"
Makefile
On my personal system, since compilation time becomes a major overhead, I also use precompiled headers. Also, for contests like Hash Code, I use OpenMP at times, so I have some flags for those in my Makefile.
Makefile
PEDANTIC_CXXFLAGS = -Iincludes/debug/includes -std=c++20 -g -Wall -Wextra -Wpedantic -Wshadow -Wformat=2 -Wfloat-equal -Wconversion -Wlogical-op -Wshift-overflow=2 -Wduplicated-cond -Wcast-qual -Wcast-align -Wno-variadic-macros -DDEBUG -DLOCAL -D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC -fsanitize=address -fsanitize=undefined -fno-sanitize-recover -fstack-protector -fsanitize-address-use-after-scope
PEDANTIC_CFLAGS = -std=c17 -g -Wall -Wextra -Wpedantic -Wshadow -Wformat=2 -Wfloat-equal -Wconversion -Wlogical-op -Wshift-overflow=2 -Wduplicated-cond -Wcast-qual -Wcast-align -Wno-variadic-macros -DDEBUG -DLOCAL -D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC -fsanitize=address -fsanitize=undefined -fno-sanitize-recover -fstack-protector -fsanitize-address-use-after-scope
NORMAL_CXXFLAGS = -Iincludes -std=c++20 -O2 -DTIMING -DLOCAL -ftree-vectorize -fopt-info-vec
NORMAL_CFLAGS = -std=c17 -O2 -DTIMING -DLOCAL -ftree-vectorize -fopt-info-vec
PARALLEL_CXXFLAGS = -fopenmp
PARALLEL_CFLAGS = -fopenmp
D ?= 0
P ?= 0
ifeq ($(D), 1)
CXXFLAGS = $(PEDANTIC_CXXFLAGS)
CFLAGS = $(PEDANTIC_CFLAGS)
else
CXXFLAGS = $(NORMAL_CXXFLAGS)
CFLAGS = $(NORMAL_CFLAGS)
endif
ifeq ($(P), 1)
CXXFLAGS += $(PARALLEL_CXXFLAGS)
CFLAGS += $(PARALLEL_CFLAGS)
endif
clean:
find . -maxdepth 1 -type f -executable -delete
rm -f *.plist
Let me know if I missed out on something, or if I should add something here!