Я бы сделал проще логику.
Не стал бы отдельно вести учет открытых карточек.
У каждой карточки будут свойства: isOpen, isActive.
isOpen обозначает, открыта ли карточка.
isActive – возможность по ней кликнуть (то есть не скрытая в результате совпадения).
Теперь по каждому клику выполняем все проверки:
const openedCards = cards
.filter(card => card.isActive) // работаем только с активными
.filter(card => card.isOpen) // берем все открытые
if (openedCards.length == 2) { // открыто 2 карточки
if (openedCards[0].text == openedCards[1].text) { // совпали
openedCards.forEach(card => card.hide()); // скрываем
} else { // не совпали
openedCards.forEach(card => card.close()); // переворачиваем обратно
}
}
if (cards.every(card => !card.isActive)) { // Если все скрыты
alert('Game over');
}