bindbox-game/scripts/debug_matching_order.go
2026-01-27 01:33:32 +08:00

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.")
}
}
}
}
}