312 lines
8.0 KiB
Go
312 lines
8.0 KiB
Go
package main
|
|
|
|
import (
|
|
"crypto/hmac"
|
|
"crypto/sha256"
|
|
"database/sql"
|
|
"encoding/binary"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"log"
|
|
"time"
|
|
|
|
_ "github.com/go-sql-driver/mysql"
|
|
)
|
|
|
|
func main() {
|
|
dsn := "root:bindbox2025kdy@tcp(150.158.78.154:3306)/dev_game?parseTime=true"
|
|
db, err := sql.Open("mysql", dsn)
|
|
if err != nil {
|
|
log.Fatal("Connection failed:", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
orderNo := "O20260125201015731"
|
|
fmt.Printf("Querying Order: %s\n", orderNo)
|
|
|
|
var id, userID int64
|
|
var status, sourceType int32
|
|
var total, discount, actual int64
|
|
var remark, createdAt string
|
|
|
|
// Check Order
|
|
err = db.QueryRow("SELECT id, user_id, status, source_type, total_amount, discount_amount, actual_amount, remark, created_at FROM orders WHERE order_no = ?", orderNo).
|
|
Scan(&id, &userID, &status, &sourceType, &total, &discount, &actual, &remark, &createdAt)
|
|
|
|
if err != nil {
|
|
log.Fatalf("Order not found: %v", err)
|
|
}
|
|
|
|
fmt.Printf("ID: %d\nUser: %d\nStatus: %d (1=Pending, 2=Paid)\nSourceType: %d\nTotal: %d\nDiscount: %d\nActual: %d\nRemark: %s\nCreated: %s\n",
|
|
id, userID, status, sourceType, total, discount, actual, remark, createdAt)
|
|
|
|
// Check Draw Logs
|
|
rows, err := db.Query("SELECT id, reward_id, is_winner, draw_index, created_at FROM activity_draw_logs WHERE order_id = ?", id)
|
|
if err != nil {
|
|
log.Fatal("Query logs failed:", err)
|
|
}
|
|
defer rows.Close()
|
|
|
|
fmt.Println("\n--- Draw Logs ---")
|
|
count := 0
|
|
for rows.Next() {
|
|
var logID, rID, dIdx int64
|
|
var isWinner int32
|
|
var ca time.Time
|
|
rows.Scan(&logID, &rID, &isWinner, &dIdx, &ca)
|
|
fmt.Printf("LogID: %d, RewardID: %d, IsWinner: %d, DrawIndex: %d, Time: %s\n", logID, rID, isWinner, dIdx, ca)
|
|
count++
|
|
}
|
|
if count == 0 {
|
|
fmt.Println("No draw logs found.")
|
|
}
|
|
|
|
// Check Inventory (Grants)
|
|
rowsInv, err := db.Query("SELECT id, reward_id, product_id, status FROM user_inventory WHERE order_id = ?", id)
|
|
if err != nil {
|
|
log.Fatal("Query inventory failed:", err)
|
|
}
|
|
defer rowsInv.Close()
|
|
|
|
fmt.Println("\n--- Inventory (Grants) ---")
|
|
invCount := 0
|
|
for rowsInv.Next() {
|
|
var invID, rID, pID int64
|
|
var s int
|
|
rowsInv.Scan(&invID, &rID, &pID, &s)
|
|
fmt.Printf("InvID: %d, RewardID: %d, ProductID: %d, Status: %d\n", invID, rID, pID, s)
|
|
invCount++
|
|
}
|
|
|
|
if invCount == 0 {
|
|
fmt.Println("No inventory grants found.")
|
|
}
|
|
|
|
// Check Issue 104
|
|
fmt.Println("\n--- Issue 104 ---")
|
|
var actID int64
|
|
var issueNumber string
|
|
err = db.QueryRow("SELECT activity_id, issue_number FROM activity_issues WHERE id = 104").Scan(&actID, &issueNumber)
|
|
if err != nil {
|
|
fmt.Printf("Issue query failed: %v\n", err)
|
|
} else {
|
|
fmt.Printf("Issue Number: %s, ActivityID: %d\n", issueNumber, actID)
|
|
// Query Activity by ID
|
|
var actName, playType string
|
|
err = db.QueryRow("SELECT name, play_type FROM activities WHERE id = ?", actID).Scan(&actName, &playType)
|
|
if err != nil {
|
|
fmt.Printf("Activity query failed: %v\n", err)
|
|
} else {
|
|
fmt.Printf("Activity: %s, PlayType: %s\n", actName, playType)
|
|
|
|
// Reconstruct Game
|
|
fmt.Println("\n--- Reconstructing Game ---")
|
|
|
|
// Fetch Draw Log
|
|
var drawLogID, issueID int64
|
|
err = db.QueryRow("SELECT id, issue_id FROM activity_draw_logs WHERE order_id = ?", id).Scan(&drawLogID, &issueID)
|
|
if err != nil {
|
|
log.Fatal("Draw log not found:", err)
|
|
}
|
|
|
|
// Fetch Receipt
|
|
var subSeedHex, position string
|
|
err = db.QueryRow("SELECT server_sub_seed, client_seed FROM activity_draw_receipts WHERE draw_log_id = ?", drawLogID).Scan(&subSeedHex, &position)
|
|
if err != nil {
|
|
log.Printf("Receipt not found: %v", err)
|
|
return
|
|
}
|
|
|
|
fmt.Printf("Receipt Found. SubSeed: %s, Position (ClientSeed): %s\n", subSeedHex, position)
|
|
|
|
serverSeed, err := hex.DecodeString(subSeedHex)
|
|
if err != nil {
|
|
log.Fatal("Invalid seed hex:", err)
|
|
}
|
|
|
|
// Fetch Card Configs
|
|
rowsCards, err := db.Query("SELECT code, name, quantity FROM matching_card_types WHERE status=1 ORDER BY sort ASC")
|
|
if err != nil {
|
|
log.Fatal("Card types query failed:", err)
|
|
}
|
|
defer rowsCards.Close()
|
|
|
|
type CardTypeConfig struct {
|
|
Code string
|
|
Name string
|
|
Quantity int32
|
|
}
|
|
var configs []CardTypeConfig
|
|
for rowsCards.Next() {
|
|
var c CardTypeConfig
|
|
rowsCards.Scan(&c.Code, &c.Name, &c.Quantity)
|
|
configs = append(configs, c)
|
|
}
|
|
|
|
// Create Game Logic
|
|
fmt.Println("Simulating Game Logic...")
|
|
|
|
cardIDCounter := int64(0)
|
|
type MatchingCard struct {
|
|
ID string
|
|
Type string
|
|
}
|
|
|
|
var deck []*MatchingCard
|
|
for _, cfg := range configs {
|
|
for i := int32(0); i < cfg.Quantity; i++ {
|
|
cardIDCounter++
|
|
deck = append(deck, &MatchingCard{
|
|
ID: fmt.Sprintf("c%d", cardIDCounter),
|
|
Type: cfg.Code,
|
|
})
|
|
}
|
|
}
|
|
|
|
// SecureShuffle
|
|
secureRandInt := func(max int, context string, nonce *int64) int {
|
|
*nonce++
|
|
message := fmt.Sprintf("%s|nonce:%d", context, *nonce)
|
|
mac := hmac.New(sha256.New, serverSeed)
|
|
mac.Write([]byte(message))
|
|
sum := mac.Sum(nil)
|
|
val := binary.BigEndian.Uint64(sum[:8])
|
|
return int(val % uint64(max))
|
|
}
|
|
|
|
nonce := int64(0)
|
|
n := len(deck)
|
|
for i := n - 1; i > 0; i-- {
|
|
j := secureRandInt(i+1, fmt.Sprintf("shuffle:%d", i), &nonce)
|
|
deck[i], deck[j] = deck[j], deck[i]
|
|
}
|
|
|
|
// Distribute to Board (first 9)
|
|
board := make([]*MatchingCard, 9)
|
|
for i := 0; i < 9; i++ {
|
|
if len(deck) > 0 {
|
|
board[i] = deck[0]
|
|
deck = deck[1:]
|
|
}
|
|
}
|
|
|
|
fmt.Printf("Board Types: ")
|
|
for _, c := range board {
|
|
if c != nil {
|
|
fmt.Printf("%s ", c.Type)
|
|
}
|
|
}
|
|
fmt.Println()
|
|
|
|
// SimulateMaxPairs
|
|
// Reconstruct allCards (Board + Deck)
|
|
allCards := make([]*MatchingCard, 0, len(board)+len(deck))
|
|
for _, c := range board {
|
|
if c != nil {
|
|
allCards = append(allCards, c)
|
|
}
|
|
}
|
|
allCards = append(allCards, deck...)
|
|
|
|
selectedType := position
|
|
hand := make([]*MatchingCard, 9)
|
|
copy(hand, allCards[:9])
|
|
deckIndex := 9
|
|
chance := int64(0)
|
|
for _, c := range hand {
|
|
if c != nil && c.Type == selectedType {
|
|
chance++
|
|
}
|
|
}
|
|
|
|
fmt.Printf("Selected Type: %s, Initial Chance: %d\n", selectedType, chance)
|
|
|
|
totalPairs := int64(0)
|
|
|
|
guard := 0
|
|
for guard < 1000 {
|
|
guard++
|
|
|
|
// canEliminate
|
|
counts := make(map[string]int)
|
|
pairType := ""
|
|
for _, c := range hand {
|
|
if c == nil {
|
|
continue
|
|
}
|
|
counts[c.Type]++
|
|
if counts[c.Type] >= 2 {
|
|
pairType = c.Type
|
|
break
|
|
}
|
|
}
|
|
|
|
if pairType != "" {
|
|
// Eliminate
|
|
first, second := -1, -1
|
|
for i, c := range hand {
|
|
if c == nil || c.Type != pairType {
|
|
continue
|
|
}
|
|
if first < 0 {
|
|
first = i
|
|
} else {
|
|
second = i
|
|
break
|
|
}
|
|
}
|
|
if first >= 0 && second >= 0 {
|
|
newHand := make([]*MatchingCard, 0, len(hand)-2)
|
|
for i, c := range hand {
|
|
if i != first && i != second {
|
|
newHand = append(newHand, c)
|
|
}
|
|
}
|
|
hand = newHand
|
|
totalPairs++
|
|
chance++
|
|
continue
|
|
}
|
|
}
|
|
|
|
// Draw
|
|
if chance > 0 && deckIndex < len(allCards) {
|
|
newCard := allCards[deckIndex]
|
|
hand = append(hand, newCard)
|
|
deckIndex++
|
|
chance--
|
|
continue
|
|
}
|
|
|
|
break
|
|
}
|
|
|
|
fmt.Printf("Simulation Finished. Total Pairs: %d\n", totalPairs)
|
|
|
|
// Check Rewards
|
|
rowsRewards, err := db.Query("SELECT id, min_score, product_id, quantity, level FROM activity_reward_settings WHERE issue_id = ?", issueID)
|
|
if err != nil {
|
|
log.Printf("Query rewards failed: %v", err)
|
|
} else {
|
|
defer rowsRewards.Close()
|
|
fmt.Println("\n--- Matching Rewards ---")
|
|
found := false
|
|
for rowsRewards.Next() {
|
|
var rwID, minScore, pID int64
|
|
var qty, level int32
|
|
rowsRewards.Scan(&rwID, &minScore, &pID, &qty, &level)
|
|
if int64(minScore) == totalPairs {
|
|
var pName string
|
|
_ = db.QueryRow("SELECT name FROM products WHERE id=?", pID).Scan(&pName)
|
|
fmt.Printf("MATCH! RewardID: %d, ProductID: %d (%s), Qty: %d, Level: %d\n", rwID, pID, pName, qty, level)
|
|
found = true
|
|
}
|
|
}
|
|
if !found {
|
|
fmt.Println("No matching reward found for this score.")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|