Short and Messy Polybius Square I attempted to recreate the Polybius square, also called the Polybius checkerboard, which was used in Ancient Greece for cryptography. Since it is an uncommon cipher, it is nowhere on Code Review.

Although the program technically works, it ended up very messy.

#include &lt;iostream&gt;
#include &lt;algorithm&gt;
#include &lt;vector&gt;
#include &lt;sstream&gt;
#include &lt;iterator&gt;
#include &lt;unordered_map&gt;

std::string getChoice();
std::vector&lt;std::string&gt; getCoords();

int main(){
    std::string choice = getChoice();

    if(choice == "e"){
        std::cout &lt;&lt; "Input string: ";
        std::string input;
        std::getline(std::cin, input);
        input.erase(remove_if(input.begin(), input.end(), isspace), input.end());

        std::unordered_map&lt;char, unsigned int&gt; alphaToInt = {
            {'A', 11},
            {'B', 12},
            {'C', 13},
            {'D', 14},
            {'E', 15},
            {'F', 21},
            {'G', 22},
            {'H', 23},
            {'I', 24},
            {'J', 24}, // same as I
            {'K', 25},
            {'L', 31},
            {'M', 32},
            {'N', 33},
            {'O', 34},
            {'P', 35},
            {'Q', 41},
            {'R', 42},
            {'S', 43},
            {'T', 44},
            {'U', 45},
            {'V', 51},
            {'W', 52},
            {'X', 53},
            {'Y', 54},
            {'Z', 55},
        };

        for(int i = 0; i &lt; input.length(); ++i){
            std::cout &lt;&lt; alphaToInt[input[i]] &lt;&lt; ' ';
        }
    } else{
        std::vector&lt;std::string&gt; coords = getCoords();
        std::string output;

        const std::string squareIJ[5][5] = {
            {"A", "B", "C", "D", "E"},
            {"F", "G", "H", "I", "K"},
            {"L", "M", "N", "O", "P"},
            {"Q", "R", "S", "T", "U"},
            {"V", "W", "X", "Y", "Z"},
        };

        for(int i = 0; i &lt; coords.size(); ++i){
            std::string coord = coords[i];
            output.append(squareIJ[coord[0] - '1'][coord[1] - '1']);
        }

        std::cout &lt;&lt; output;

    }

    std::cout &lt;&lt; '\n';
    return 0;
}

std::string getChoice(){
    std::string choice;
    do{
        std::cout &lt;&lt; "Encrypt or decrypt [e/d] = ";
        std::getline(std::cin, choice);
        if(choice == "E") choice = "e";
        std::transform(choice.begin(), choice.end(), choice.begin(), ::tolower);
    } while(choice != "e" &amp;&amp; choice != "d");

    return choice;
}

std::vector&lt;std::string&gt; getCoords(){
    std::cout &lt;&lt; "Coordinates (separate with spaces): ";
    std::string strCoords;
    std::getline(std::cin, strCoords);
    std::stringstream streamCoords(strCoords);
    std::istream_iterator&lt;std::string&gt; begin(streamCoords);
    std::istream_iterator&lt;std::string&gt; end;
    std::vector&lt;std::string&gt; coords(begin, end);
    return coords;
}


The Polybius square is a simple way to assign characters numbers, and then "encrypt" and "decrypt" based off of those numbers. As shown in the unordered map and char array, the numbers correspond to the row and column on a 5x5 square:



The getChoice() function simply gets the e or d character to choose encryption or decryption.

The getCoords() function gets a string, and then tokenizes the string into a vector of strings, each corresponding to the coordinates of a character.

Example of "encryption":

Encrypt or decrypt [e/d] = e
Input string: BAT
12 11 44


Example of "decryption":

Encrypt or decrypt [e/d] = d
Coordinates (separate with spaces): 12 11 44
BAT


This code, in my opinion, is quite messy. How can I improve it?
It's not that messy IMO, but there are a few things which you could have done better:


Always compile code with every warning turned on, and fix them. When compiled with -Wall, your code produces 2 same warnings:

warning: comparison between signed and unsigned integer expressions [-Wsign-compare]


You should fix them.
Your choice variable really wants to be an enum instance:

enum class mode {
    encrypt, decrypt
};

You have some redundant code:

if(choice == "E") choice = "e";
// redundant if in the next line your lower casing every character in 'choice'

const correctness: alphaToInt can and should be const, as you're not going to modify it no matter what.
getCoords can be reduced considerably (it looks too bloated otherwise IMO):

std::vector&lt;std::string&gt; getCoords(){
    std::cout &lt;&lt; "Coordinates (separate with spaces): ";
    std::string strCoords;
    std::getline(std::cin, strCoords);

    std::stringstream streamCoords(strCoords);
    return{ std::istream_iterator&lt;std::string&gt;{ streamCoords }, std::istream_iterator&lt;std::string&gt;{} };
} 

squareIJ doesn't have to be a std::string[5][5], it can be a simple char[5][5]. Don't allocate memory if you don't have too (this might actually not allocate memory for the std::string, due to SSO actually). 
Use auto to simply some variable definitions if you want to:

// std::vector&lt;std::string&gt; coords = getCoords();
auto coords = getCoords();

// ...

Use ranged-based loop instead of index loop. For example, 

for(int i = 0; i &lt; coords.size(); ++i){
    std::string coord = coords[i];
    output.push_back(squareIJ[coord[0] - '1'][coord[1] - '1']);
}


becomes

for (const auto&amp; coord : coords)
    output.push_back(squareIJ[coord[0] - '1'][coord[1] - '1']);

Include every necessary headers, you forgot &lt;cctype&gt; for std::isspace.
If you expect every character to be upper case, either (1) tell the user (not recommended) or (2) upper case the string yourself:

std::transform(input.begin(), input.end(), input.begin(), ::toupper);