Skip to content

Commit

Permalink
Implemented multiple gacha
Browse files Browse the repository at this point in the history
  • Loading branch information
iamananya committed Jun 6, 2023
1 parent 76f8c12 commit d01819c
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 49 deletions.
Binary file modified cmd/main/main
Binary file not shown.
2 changes: 1 addition & 1 deletion pkg/config/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ var (
)

func Connect() {
d, err := gorm.Open("mysql", "root:1201@/gacharest?charset=utf8&parseTime=True&loc=Local")
d, err := gorm.Open("mysql", "root:1201@/multiple_gacha?charset=utf8&parseTime=True&loc=Local")
if err != nil {
panic(err)
}
Expand Down
97 changes: 62 additions & 35 deletions pkg/controllers/gacha-controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ func ListCharacters(w http.ResponseWriter, r *http.Request) {
func HandleGachaDraw(w http.ResponseWriter, r *http.Request) {
user, ok := r.Context().Value("user").(models.User)
if !ok {
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
http.Error(w, "Internal Server Error to retrieve user", http.StatusInternalServerError)
return
}
fmt.Print(user.Name)
Expand All @@ -138,27 +138,45 @@ func HandleGachaDraw(w http.ResponseWriter, r *http.Request) {
return
}
fmt.Printf("Received request: %+v\n", reqBody)

gacha, err := models.GetGachaByID(reqBody.GachaID)
if err != nil {
http.Error(w, "Invalid gacha ID", http.StatusBadRequest)
return
}

characters, err := models.GetAllCharacters()
if err != nil {
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
http.Error(w, "Internal Server Error in characters", http.StatusInternalServerError)
return
}
characterPool := GenerateCharacterPool(characters)
// fmt.Println(characters)
var characterPointers []*models.Character
fmt.Println("Appended characters")
for _, character := range characters {
charac := character
characterPointers = append(characterPointers, &charac)
}
fmt.Println("character pointers", characterPointers)
characterPool := generateCharacterPool(characterPointers, gacha.RarityR, gacha.RaritySR, gacha.RaritySSR, reqBody.Times)
// fmt.Println("character pool", characterPool)
response := models.GachaDrawResponse{
Results: make([]models.CharacterResponse, reqBody.Times),
}
// Create a slice to store the user characters for batch insert
userCharacters := make([]*models.UserCharacter, reqBody.Times)
// fmt.Println("User Characters", userCharacters)

for i := 0; i < reqBody.Times; i++ {
character, err := DrawCharacter(characterPool)
character, err := drawCharacter(characterPool)
if err != nil {
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
http.Error(w, "Internal Server Error in drawing characters", http.StatusInternalServerError)
return
}
userCharacter := models.UserCharacter{
UserID: user.ID,
CharacterID: character.ID,
GachaID: gacha.ID,
AttackPower: character.AttackPower,
Defense: character.Defense,
Speed: character.Speed,
Expand All @@ -181,7 +199,7 @@ func HandleGachaDraw(w http.ResponseWriter, r *http.Request) {
}
// Batch insert user characters into the database
if err := models.CreateUserCharacterBatch(userCharacters); err != nil {
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
http.Error(w, "Internal Server Error in creating batch", http.StatusInternalServerError)
return
}

Expand All @@ -196,61 +214,70 @@ func HandleGachaDraw(w http.ResponseWriter, r *http.Request) {
w.Write(respBody)
}

func GenerateCharacterPool(characters []models.Character) []models.Character {
characterPool := make([]models.Character, 0)
probabilityMap := make(map[uint]int)
func generateCharacterPool(characters []*models.Character, rarityR, raritySR, raritySSR float64, numDraws int) []*models.Character {
characterPool := make([]*models.Character, 0)

characters_all := make([]string, 0)

rarityProbabilities := map[string]float64{
"SSR": raritySSR,
"SR": raritySR,
"R": rarityR,
}

for _, character := range characters {
rarity := character.Rarity
var probability int

switch rarity {
case "SSR":
probability = 5
case "SR":
probability = 15
case "R":
probability = 80
}
draws := rarityProbabilities[rarity]
rarityDraws := int(draws * float64(numDraws))

probabilityMap[character.ID] = probability
for i := 0; i < rarityDraws; i++ {
characters_all = append(characters_all, character.Name)
}
}

for characterID, probability := range probabilityMap {
character := FindCharacterByID(characters, characterID)
for i := 0; i < probability; i++ {
characterPool = append(characterPool, character)
poolSize := len(characters_all)
for i := 0; i < poolSize; i++ {
randIndex, err := rand.Int(rand.Reader, big.NewInt(int64(poolSize-i)))
if err != nil {
fmt.Printf("Failed to generate random number: %s\n", err.Error())
return characterPool
}
index := int(randIndex.Int64())

characterName := characters_all[index]
character := findCharacterByName(characters, characterName)
characterPool = append(characterPool, character)

// Remove the selected character from the pool
// characters_all = append(characters_all[:index], characters_all[index+1:]...)
characters_all[index] = characters_all[poolSize-i-1]
characters_all = characters_all[:poolSize-i-1]

}

return characterPool
}

func FindCharacterByID(characters []models.Character, characterID uint) models.Character {
func findCharacterByName(characters []*models.Character, name string) *models.Character {
for _, character := range characters {
if character.ID == characterID {
if character.Name == name {
return character
}
}
return models.Character{}
return nil
}

func DrawCharacter(characterPool []models.Character) (models.Character, error) {
func drawCharacter(characterPool []*models.Character) (*models.Character, error) {
poolSize := len(characterPool)
if poolSize == 0 {
return models.Character{}, errors.New("empty character pool")
return nil, errors.New("empty character pool")
}

// Randomly select a character from the pool
randIndex, err := rand.Int(rand.Reader, big.NewInt(int64(poolSize)))
if err != nil {
return models.Character{}, errors.New("failed to generate random number")
return nil, errors.New("failed to generate random number")
}
index := int(randIndex.Int64())
character := characterPool[index]

// Remove the selected character from the pool
characterPool = append(characterPool[:index], characterPool[index+1:]...)

return character, nil
}
82 changes: 69 additions & 13 deletions pkg/models/gacha.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package models

import (
"crypto/rand"
"errors"
"fmt"
"math/big"

"github.com/iamananya/ginco-task/pkg/config"
Expand All @@ -18,23 +20,25 @@ type User struct {
}
type Character struct {
gorm.Model
Name string `gorm:"type:varchar(30);size:30" json:"name"`
AttackPower int `gorm:"column:attack_power" json:"attack_power"`
Defense int `gorm:"column:defense" json:"defense"`
Speed int `json:"speed"`
HitPoints int `gorm:"column:hit_points" json:"hit_points"`
CriticalHitRate float64 `gorm:"column:critical_hit_rate" json:"critical_hit_rate"`
ElementalAffinity string `json:"elemental_affinity"`
Rarity string `json:"rarity"`
Synergy bool `json:"synergy"`
Evolution bool `json:"evolution"`
Users []UserCharacter `gorm:"foreignKey:CharacterID" json:"users"`
Name string `gorm:"type:varchar(30);size:30" json:"name"`
AttackPower int `gorm:"column:attack_power" json:"attack_power"`
Defense int `gorm:"column:defense" json:"defense"`
Speed int `json:"speed"`
HitPoints int `gorm:"column:hit_points" json:"hit_points"`
CriticalHitRate float64 `gorm:"column:critical_hit_rate" json:"critical_hit_rate"`
ElementalAffinity string `json:"elemental_affinity"`
Rarity string `json:"rarity"`
Synergy bool `json:"synergy"`
Evolution bool `json:"evolution"`
Users []*UserCharacter `gorm:"foreignKey:CharacterID" json:"users"`
GachaCharacters []*GachaCharacter
}

type UserCharacter struct {
gorm.Model
UserID uint `gorm:"index" json:"user_id"`
CharacterID uint `gorm:"index" json:"character_id"`
GachaID uint `gorm:"index" json:"gacha_id"`
AttackPower int `gorm:"column:attack_power" json:"attack_power"`
Defense int `gorm:"column:defense" json:"defense"`
Speed int `json:"speed"`
Expand All @@ -45,14 +49,31 @@ type UserCharacter struct {
Synergy bool `json:"synergy"`
Evolution bool `json:"evolution"`
}
type Gacha struct {
gorm.Model
Name string `gorm:"type:varchar(30);size:30" json:"name"`
RarityR float64 `gorm:"column:rarity_r" json:"rarity_r"`
RaritySR float64 `gorm:"column:rarity_sr" json:"rarity_sr"`
RaritySSR float64 `gorm:"column:rarity_ssr" json:"rarity_ssr"`
Characters []*GachaCharacter
}

type GachaCharacter struct {
gorm.Model
GachaID uint `gorm:"index" json:"gacha_id"`
CharacterID uint `gorm:"index" json:"character_id"`
Character *Character
}
type GachaResult struct {
gorm.Model
CharacterID uint `gorm:"index" json:"character_id"`
CharacterName string `json:"character_name"`
}

type GachaDrawRequest struct {
Times int `json:"times"`
UserID int `json:"user_id"`
Times int `json:"times"`
GachaID int `json:"gacha_id"`
}

type GachaDrawResponse struct {
Expand Down Expand Up @@ -88,6 +109,25 @@ func init() {
if err := db.AutoMigrate(&GachaResult{}).Error; err != nil {
panic("Failed to migrate GachaResult model: " + err.Error())
}
// Add necessary columns to existing tables
db.Exec("ALTER TABLE gacha_results ADD COLUMN IF NOT EXISTS gacha_id INT")
db.Exec("ALTER TABLE gacha_results ADD COLUMN IF NOT EXISTS character_id INT")

// Migrate gacha tables
if err := db.AutoMigrate(&Gacha{}).Error; err != nil {
panic("Failed to migrate Gacha model: " + err.Error())
}
if err := db.AutoMigrate(&GachaCharacter{}).Error; err != nil {
panic("Failed to migrate GachaCharacter model: " + err.Error())
}

// Add necessary columns to existing tables
db.Exec("ALTER TABLE gacha_characters ADD COLUMN IF NOT EXISTS gacha_id INT")
db.Exec("ALTER TABLE gacha_characters ADD COLUMN IF NOT EXISTS character_id INT")

// Add 'gacha_id' column to user_characters table
db.Exec("ALTER TABLE user_characters ADD COLUMN IF NOT EXISTS gacha_id INT")
db.Exec("ALTER TABLE user_characters ADD CONSTRAINT fk_gacha_id FOREIGN KEY (gacha_id) REFERENCES gachas(id) ON DELETE RESTRICT ON UPDATE RESTRICT")
}

func (u *User) CreateUser() (*User, error) {
Expand Down Expand Up @@ -136,7 +176,6 @@ func GetAllCharacters() ([]Character, error) {
return characters, nil
}
func CreateUserCharacterBatch(userCharacters []*UserCharacter) error {
db := config.GetDB()
tx := db.Begin()
batchSize := 1000 // Adjust the batch size as per your requirements

Expand Down Expand Up @@ -174,3 +213,20 @@ func (gr *GachaResult) SaveGachaResult() error {
err := db.Create(gr).Error
return err
}

func GetGachaByID(gachaID int) (*Gacha, error) {
// Create a new Gacha object to store the retrieved data
gacha := &Gacha{}

// Retrieve the gacha by ID from the database
if err := db.First(gacha, gachaID).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
// Handle case when no gacha with the specified ID is found
return nil, fmt.Errorf("gacha with ID %d not found", gachaID)
}
// Handle other errors that may occur during the database query
return nil, err
}

return gacha, nil
}

0 comments on commit d01819c

Please sign in to comment.