아니요. 감사합니다. 안녕히 계세요.
좋아, 우선 클릭 미끼 스타일의 제목에 대해 사과드립니다. 때때로 데모에 대한 아이디어를 얻었을 때 작동하지 않습니다. 하지만 때로는 재미있고 흥미로운 방법으로 해결되지 않을 때도 있고, 어쨌든 공유하는 것이 좋은 생각이라고 생각합니다. (또한 내가 뭔가 잘못했기 때문에 제대로 되지 않았을 가능성도 항상 존재합니다!) 오늘의 데모는 그에 대한 완벽한 예라고 생각합니다.
올해 초 저는 Alpine.js와 아주 뛰어난 Deck of Cards API 를 사용하여 블랙잭 게임을 만들었습니다. 어제 저는 게임에 대해 생각하다가 게임을 할 때 생성 AI를 사용하여 도움을 요청하면 어떻게 될까요? 예를 들어, 저는 제 게임 에 가서 이것을 보았습니다:
Google의 PaLM Makersuite 앱에서 나는 다음과 같이 썼습니다.
나는 블랙잭을 하고 있는데 딜러는 6개의 다이아몬드를 보여주고 있습니다. 나는 클럽 잭과 하트 잭을 가지고 있습니다. 때려야 할까, 아니면 머물러야 할까?
그리고 이런 응답을 받았습니다.
당신은 20을 갖고 있는데, 그것은 좋은 핸드이다. 딜러는 평균보다 낮은 16개를 보유하고 있습니다. 맞으면 버스트 카드를 얻을 위험이 있습니다. 따라서 딜러가 파산하기를 바라는 것이 더 낫습니다.
네, 꽤 분명합니다. 잭 두 개로는 절대 칠 수 없습니다. 하지만 게임의 원리를 강화하는 설명적인 반응이 마음에 들었습니다. 저는 GenAI에게 도움을 요청할 수 있는 버튼을 게임에 추가하면 어떨까라고 생각했습니다. 내가 한 방법은 다음과 같습니다.
프런트엔드에서는 UI 변경을 최소화했습니다. 저는 간단한 버튼을 추가하는 것부터 시작했습니다.
이것을 클릭하면 두 가지 중요한 작업을 수행해야 합니다. 첫째, 게임 상태를 프롬프트로 '번역'해야 합니다. 그런 다음 이를 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); },
나는 플레이어 카드와 PC 카드를 그들의 손을 나타내는 카드 배열을 포함하는 두 개의 변수에 저장합니다. 각 카드에는 Jack처럼 숫자나 이름인 '값'이 있습니다. 각 카드에는 슈트도 있습니다. 이론적으로 그것은 아마도 중요한 정보가 아닐 수도 있지만, 나쁠 것은 없을 것이라고 생각했습니다. 다음은 게임에서 생성된 프롬프트 예시입니다.
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개였습니다. 나는 스페이드의 여왕과 다이아몬드의 8개를 가지고 있었습니다. 도움을 요청했을 때 다음과 같은 결과를 얻었습니다.
딜러의 업카드는 7이며 이는 "하드" 17입니다. 즉, 딜러는 귀하가 무엇을 하든 관계 없이 서 있어야 합니다. 당신은 "소프트" 19를 가지고 있습니다. 이는 1이나 11로 셀 수 있는 에이스가 있다는 것을 의미합니다. 당신은 그대로 있어야 합니다.
알았어, 물론이지, 난 여기 있어야 하는데 , 도대체 무슨 문제가 있는 거지? 19개 있어요? 딜러는 17명인가요? 그리고 그것은 좋아집니다. 한번은 18개를 상영했을 때 다음과 같은 결과를 얻었습니다.
때리는 걸 추천드려요. 총합이 18이면 블랙잭에서 승리하는 패인 21과 단 1점 차이가 납니다. 딜러의 하트 7개는 총 17개를 제공하며 이는 21개 임계값보다 낮습니다. 따라서 딜러가 히트하면 버스트하여 승리할 가능성이 높습니다. 그러나 계속 머무르면 핸드를 향상시킬 수 없으며 딜러가 파산하기를 바랄 수밖에 없습니다.
18번을 칩니다. 저는 전문 도박꾼은 아니지만 그것이 나쁜 조언이라고 확신합니다.
이로 인해 내 프롬프트에 대해 좀 더 생각하게되었습니다. AI가 혼란스러울 수 있다는 우려로 슈트를 낮추는 등 약간의 조정을 했습니다. 저는 Makersuite의 몇 가지 옵션을 반복해 보았으며, 지속적으로 기본적인 수학 문제에 어려움을 겪는 것 같았습니다. 내가 명시적으로 말했을 때에도:
나는 클럽 잭(10개 상당)과 하트 8개(8개 상당)를 가지고 있습니다.
여전히 내가 19살이라고 생각했습니다. 나는 이것으로 더 나은 행운을 얻었습니다.
나는 클럽 잭(10개 상당)과 하트 8개(8개 상당)를 가지고 있습니다. 내 핸드의 총합은 18입니다.
그리고 나는 또한 이것을 시도했습니다:
나는 클럽 에이스와 하트 8개를 가지고 있습니다. 내 핸드의 총합은 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); },
내 블랙잭 게임에는 두 개의 값 lowCount
(여기서 에이스는 1로 처리됨)와 highCount
(최대 1개의 에이스가 11로 간주됨)가 포함된 개체를 반환하는 유틸리티 함수인 getCount
있습니다. 이를 통해 총액에 대한 프롬프트 정보를 추가합니다.
이것은 꽤 도움이 되었지만 여전히 때때로 망상적인 반응을 보였습니다.
그럼... 도박 테이블에서 조언을 제공하는 술 취한 친구를 생각해 보세요.
이 버전의 데모를 공개적으로 공유하고 싶습니다. 물론 코드도 공유할 수 있지만 비용이 청구되지 않도록 Pipedream 워크플로를 비활성화하겠습니다. 여기에서 프런트엔드 코드를 자유롭게 살펴보세요.
https://codepen.io/cfjedimaster/pen/QWYpQjY/905e31ed6d9e3539c041044b308f5f06?editors=1011
언제나처럼, 여러분의 생각을 알려주세요!
여기에도 게시되었습니다.