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:

  1. 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.
  2. 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”.
  3. 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
  1. set nocp: required for some technical issues, most prominently when you load a vimrc with vim -u <vimrc_path> or to avoid bugs when you just use vi instead. Basically, always keep it in your vimrc.

  2. filetype plugin indent on: turns on filetype detection, plugins and indentation. If you’re more curious about what this does, read this.

  3. syntax enable: turns on syntax highlighting.

  4. 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.

  5. source $VIMRUNTIME/delmenu.vim and source $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 set mouse=a mousem=popup like I did in the beginner vimrc.

  6. 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 clipboards

  7. colorscheme 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.

  8. let mapleader = ",": sets the leader key to a comma. Not really important if you don’t use it.

  9. 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.

  10. map 0 ^: when you input 0 in normal mode, it’ll go to the first non-whitespace character.

  11. packadd termdebug, let g:termdebug_popup=0 and let 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.

  12. let &t_ut’’=: fixes some background-color related issues that I find annoying, but it’s barely noticeable.

  13. let &t_SI"\<Esc>[6 q", =let &t_SR"\<Esc>[4 q"= and let &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
  1. CXXFLAGS is a variable that refers to the flags used to compile a C++ file.
  2. D is a variable that we defined, and it is equal to 0 by default. If instead of running make a, we run make 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):

  1. Run gdb <executable_name>
  2. If you have an input file called in.txt, type run < 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.
  3. 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!