Simple Rock Paper Scissors
Looks pretty good at first glance! I'd say your main problem is &quot;#define ONE 1&quot; — you've gone out of your way to parameterize things that can't actually be changed in real life without massive changes elsewhere in the game logic. For example, you've set
enum Items { Rock, Paper, Scissors, ItemsCount };

which looks awesome... until someone decides to actually use that flexibility by changing it to
enum Items { Rock, Paper, Scissors, Lizard, Spock, ItemsCount };

and suddenly nothing compiles anymore. (They also have to change the local variables table and data and binding_item inside main, and the text of the cout inside user_choice, and maybe some more stuff too. By the way: variable names are important! data and table are not good names!)
The over-parameterization antipattern tends to travel along with the std::array antipattern — see &quot;The 'array size constant' antipattern&quot; (2020-08-06). Somehow, people who've caught the &quot;named constants &gt; magic numbers&quot; virus also tend to have caught the &quot;std::array &gt; core language&quot; virus as well. Where you write
template&lt;typename T, std::size_t N&gt;
using Matrix = std::array&lt;std::array&lt;T, N&gt;, N&gt;;

const Matrix&lt;Winner, Winner::WinnerCount&gt; table
{
    {  // Rock      Paper      Scissors
        { Tie,      Computer,  Player   }, // Rock
        { Player,   Tie,       Computer }, // Paper
        { Computer, Player,    Tie      }, // Scissors
    }
};

an expert C++ programmer would probably just write
Winner table[3][3] = {
    // Rock      Paper      Scissors
    {  Tie,      Computer,  Player   }, // Rock
    {  Player,   Tie,       Computer }, // Paper
    {  Computer, Player,    Tie      }, // Scissors        
};

Incidentally, did you notice your bug above? You dimensioned your table by WinnerCount, but you index into it using values of Item. You're simply getting lucky that WinnerCount (win, lose, tie) happens to be the same as ItemCount (rock, paper, scissors).

Be aware that
std::mt19937 engine{ std::random_device()() };

is a relatively low-entropy way to initialize a Mersenne Twister: it has 19937 bits of state, and you're seeding it with only 32 random bits. But C++ doesn't offer any concise way to seed a PRNG properly, so actually what you've got is the state of the art.

const Items computer = static_cast&lt;Items&gt;(dist(engine));

Throughout, you use plural words where singular would be more appropriate, and relatively abstract nouns (data, table, computer) where more concrete ones would be appropriate. I'd write:
Item computers_choice = static_cast&lt;Item&gt;(dist(engine));

indicating that what we've got now is not a computer but rather the computer player's choice of what to throw; and it is not multiple Items but rather a single Item. (I'd probably rename Item to Gesture, too.)

Your user_choice function returns std::string, which you then have to parse again in main. It should just return the Item it extracted, directly, instead of the string.
Also, the logic around the is_input_valid boolean seems pretty baroque. Think about how the code could be simplified by using a return or break statement inside the loop.
if (std::getline(std::cin, result)) {
    if (map_of_strings_to_gestures.contains(result)) {
        return map_of_strings_to_gestures.at(result);
    }
}
std::cout &lt;&lt; &quot;Not valid input.\n&quot;;
// and go around again

Also, try hitting Ctrl-D (or Ctrl-Z on Windows) at this prompt. That'll close stdin, which makes cin.fail() true, which means the input isn't valid... and then your code just goes into an infinite loop, doesn't it? I think you actually want to break out of the loop when you see EOF or an error — not stay in the loop!

For configuring the gestures, I think you should investigate X-macros. Here's my take: https://godbolt.org/z/8qhdcPWT5 The entirety of the &quot;configuration&quot; for the game is expressed in a single table at the top of the code:
#define GESTURES \
    X(Rock,     &quot;rock&quot;,     BEATS(Scissors)) \
    X(Scissors, &quot;scissors&quot;, BEATS(Paper)) \
    X(Paper,    &quot;paper&quot;,    BEATS(Rock))

and I've also tested it with this configuration:
#define GESTURES \
    X(Rock,     &quot;rock&quot;,     BEATS(Scissors) BEATS(Lizard)) \
    X(Scissors, &quot;scissors&quot;, BEATS(Paper) BEATS(Lizard)) \
    X(Paper,    &quot;paper&quot;,    BEATS(Rock) BEATS(Spock)) \
    X(Lizard,   &quot;lizard&quot;,   BEATS(Spock) BEATS(Paper)) \
    X(Spock,    &quot;spock&quot;,    BEATS(Scissors) BEATS(Rock))

(My version also eliminates the dependencies on &lt;map&gt;, &lt;algorithm&gt;, and of course &lt;limits&gt;.)

An obvious &quot;next step&quot; for this program would be to refactor it so that you can print a customized verb for each outcome:
Enter your choice (rock, scissors, paper): rock
You entered: rock
Computer chose: paper
Paper covers rock — you lose!

Enter your choice (rock, scissors, paper): scissors
You entered: scissors
Computer chose: paper
Scissors cuts paper — you win!

and so on.