Нет. Спасибо и до свидания.
Хорошо, во-первых, я прошу прощения за заголовок в стиле кликбейта. Время от времени, когда мне приходит в голову идея сделать демо, она не работает. Но иногда это не получается весело и интересно, и я считаю, что все равно стоит поделиться этим. (Кроме того, всегда есть большая вероятность, что это не сработало, потому что я сделал что-то не так!) Сегодняшнее демо, я думаю, является прекрасным примером этого.
Ранее в этом году я создал игру в блэкджек , используя Alpine.js и отличный API Deck of Cards . Вчера я думал об игре и задавался вопросом: что произойдет, если я буду использовать генеративный ИИ, чтобы попросить о помощи во время игры? Итак, например, я зашёл в свою игру и увидел это:
В приложении Google PaLM Makersuite я написал:
Я играю в блэкджек, и у дилера открыта шестерка бубен. У меня есть валет треф и валет червей. Должен ли я ударить или остаться?
И получил такой ответ:
У вас 20, это хорошая рука. У дилера 16, что ниже среднего. Если вы попадете, вы рискуете получить карту, которая вас разорит. Так что лучше остаться и надеяться, что дилер обанкротится.
Что, ок, довольно очевидно. Вы никогда не попадете с двумя валетами. Но мне понравился описательный ответ, подчеркивающий принципы игры. Я подумал — а что если добавить в игру кнопку, позволяющую обратиться за помощью к GenAI? Вот как я это сделал:
Что касается внешнего интерфейса, я оставил изменения пользовательского интерфейса минимальными. Я начал с добавления простой кнопки:
Нажав на это, нужно сделать две важные вещи. Во-первых, ему необходимо «перевести» состояние игры в подсказку; затем ему необходимо передать это в службу, которая будет обрабатывать вызов Gen AI . Я создал новую функцию, askForHelp
, которая обрабатывает это:
async askForHelp() { this.aiHelp = '<i>Asking our AI friend...</i>'; // first, "translate" the cards to English let q = `I'm playing blackjack and the dealer currently has ${this.pcCards[1].value} of ${this.pcCards[1].suit.toLowerCase()}. I've got a ${this.playerCards[0].value} of ${this.playerCards[0].suit.toLowerCase()} and a ${this.playerCards[1].value} of ${this.playerCards[1].suit.toLowerCase()}. Should I hit or should I stay?`; console.log(q); let resp = await fetch(`https://eo8dkukowewth66.m.pipedream.net?text=${encodeURIComponent(q)}`); let help = await resp.json(); /* note that help has line breaks and stuff in and in theory we should map to br or something, but im fine ignoring. */ this.aiHelp = ''; alert(help); },
Я храню карты игрока и ПК в двух переменных, которые содержат массив карт, представляющих их руки. У каждой карты есть «значение», которое представляет собой число или имя, например, Джек. У каждой карты также есть масть. Теоретически это, наверное, не критическая информация, но я решил, что это не повредит. Вот пример приглашения, сгенерированного игрой:
I'm playing blackjack and the dealer currently has 8 of clubs. I've got a 7 of spades and a QUEEN of hearts. Should I hit or should I stay?
Это передается моей внутренней службе (подробнее об этом через секунду), а затем результат отображается в предупреждении JavaScript, что на 100% ужасно UX, но, поскольку это был эксперимент, я решил, что это достаточно хорошо.
Если вам интересно, вот какой был ответ:
Оставаться. У вас всего 17, и это хорошая рука. 8 у дилера не является старшей картой, поэтому вы вряд ли проиграете, если останетесь.
Для своей серверной части я разработал быстрый рабочий процесс Pipedream . Мой рабочий процесс состоял буквально из трёх шагов.
Первый — мой триггер, конечная точка HTTP. Вы можете увидеть URL-адрес в коде внешнего интерфейса выше.
Второй шаг — это встроенное действие Google PaLM API. Все, что мне нужно было сделать, это указать ему, что использовать в качестве приглашения: {{steps.trigger.event.query.text}}
Мой последний шаг просто вернул результат:
export default defineComponent({ async run({ steps, $ }) { await $.respond({ status: 200, headers: { "Content-Type":"application/json" }, body: JSON.stringify(steps.generate_text.$return_value[0].candidates[0].output), }) }, })
Так что да. Мои первые несколько тестов прошли отлично, а потом... все пошло не так. Вот пример. У дилера была семерка червей. У меня была пиковая дама и бубновая восьмерка. Когда я обратился за помощью, я получил:
Открытая карта дилера — 7, что является «жесткой» цифрой 17. Это означает, что дилер должен остаться, независимо от того, что вы делаете. У вас «мягкая» 19, а это значит, что у вас есть туз, который может считаться как 1, так и 11. Вам следует остаться.
Хорошо, конечно, мне следует остаться, но что на самом деле? У меня 19? У дилера 17? И становится лучше. Однажды, когда у меня было 18 показов, я получил следующее:
Я бы рекомендовал ударить. Имея в общей сложности 18 очков, вы находитесь всего в одном очке от 21, что является выигрышной комбинацией в блэкджеке. Семерка червей дилера дает ему в общей сложности 17, что ниже порога в 21. Таким образом, есть большая вероятность, что дилер выиграет, если попадет, что принесет вам выигрыш. Однако, если вы останетесь, вы не сможете улучшить свою руку и вам придется надеяться, что дилер обанкротится.
Нажимаю на 18. Я не профессиональный игрок, но почти уверен, что это плохой совет.
Это заставило меня еще немного задуматься над подсказкой. Я внес некоторые изменения, например, уменьшил размер костюма, так как боялся, что это может сбить с толку ИИ. Я перебрал некоторые параметры в Makersuite, и постоянно казалось, что иногда возникают проблемы с базовыми математическими вычислениями. Даже когда я был откровенен:
У меня есть валет треф (стоит десять) и восьмерка червей (стоит 8).
Он все еще думал, что мне 19. С этим мне повезло больше:
У меня есть валет треф (стоит десять) и восьмерка червей (стоит 8). У меня общее количество рук 18.
И я также попробовал это:
У меня туз треф и восьмерка червей. Моя сумма рук 9 или 19.
И это, казалось, всегда работало правильно. Итак, помня об этом, я вернулся к генерации подсказок и обновил там свою логику. Вот обновленная версия:
async askForHelp() { this.aiHelp = '<i>Asking our AI friend...</i>'; let totalOb = this.getCount(this.playerCards); let totalStr = 'My hand has a total of '; if(totalOb.lowCount === totalOb.highCount) { totalStr += totalOb.lowCount; } else { totalStr += `${totalOb.lowCount} or ${totalOb.highCount}`; } // first, "translate" the cards to English let q = `I'm playing blackjack and the dealer currently has ${this.pcCards[1].value} of ${this.pcCards[1].suit.toLowerCase()}. I've got a ${this.playerCards[0].value} of ${this.playerCards[0].suit.toLowerCase()} and a ${this.playerCards[1].value} of ${this.playerCards[1].suit.toLowerCase()}. ${totalStr}. Should I hit or should I stay?`; console.log(q); let resp = await fetch(`https://eo8dkukowewth66.m.pipedream.net?text=${encodeURIComponent(q)}`); let help = await resp.json(); console.log('RESP', help); /* note that help has line breaks and stuff in and in theory we should map to br or something, but im fine ignoring. */ this.aiHelp = ''; alert(help); },
В моей игре в блэкджек есть служебная функция getCount
, которая возвращает объект, содержащий два значения lowCount
, где тузы считаются одним, и highCount
, где максимум один туз считается одиннадцатью. При этом добавляю к своей подсказке информацию об итоге.
Это действительно немного помогло , но я все равно иногда получал бредовые ответы.
Итак... может, считай, что это твой пьяный друг за игровым столом, дающий совет?
Я хотел бы опубликовать эту версию демо-версии публично, и я, конечно, могу поделиться кодом, но я отключаю рабочий процесс Pipedream, чтобы с меня не взимали за него плату. Не стесняйтесь посмотреть код интерфейса здесь:
https://codepen.io/cfjedimaster/pen/QWYpQjY/905e31ed6d9e3539c041044b308f5f06?editors=1011
Как всегда, дайте мне знать, что вы думаете!
Также опубликовано здесь .