ファイル
btrc-hub/backend/app/controllers/gekanator_games_controller.rb
T
2026-06-12 01:33:40 +09:00

141 行
4.0 KiB
Ruby

class GekanatorGamesController < ApplicationController
def create
return head :not_found unless current_user&.admin?
guessed_post_id = params.require(:guessed_post_id)
correct_post_id = params[:correct_post_id].presence
answers = params.require(:answers).as_json
game = GekanatorGame.new(
user: current_user,
guessed_post_id:,
correct_post_id:,
won: correct_post_id.present? && guessed_post_id.to_i == correct_post_id.to_i,
question_count: answers.length,
answers:)
if game.save
render json: { id: game.id }, status: :created
else
render json: { errors: game.errors.full_messages }, status: :unprocessable_entity
end
end
def extra_questions
return head :not_found unless current_user&.admin?
game = GekanatorGame.find_by(id: params[:id])
return head :not_found unless game
questions =
GekanatorQuestion
.accepted
.includes(:gekanator_question_examples)
.where(kind: 'post_similarity', source: 'user_suggested')
.to_a
selected = weighted_sample_questions(
questions,
post_id: game.correct_post_id,
limit: 2)
render json: {
questions: selected.map { |question| extra_question_json(question) }
}
end
def extra_question_answers
return head :not_found unless current_user&.admin?
game = GekanatorGame.find_by(id: params[:id])
return head :not_found unless game
answer_params = params.require(:answers)
if !answer_params.is_a?(Array)
return render_validation_error fields: { answers: ['配列で指定してください.'] }
end
answers = answer_params.map { |answer|
{
question_id: answer.require(:question_id).to_i,
answer: answer.require(:answer)
}
}
questions = GekanatorQuestion.where(id: answers.map { _1[:question_id] })
question_by_id = questions.index_by(&:id)
if questions.length != answers.length
return render_validation_error fields: { answers: ['質問が見つかりません.'] }
end
if questions.any? { |question| question.status != 'accepted' || question.kind != 'post_similarity' }
return render_validation_error fields: { answers: ['質問が不正です.'] }
end
ActiveRecord::Base.transaction do
answers.each do |item|
question = question_by_id[item[:question_id]]
example =
GekanatorQuestionExample.find_or_initialize_by(
gekanator_question: question,
post: game.correct_post,
user: current_user)
example.record_answer!(
answer: item[:answer],
source: 'post_game_extra',
gekanator_game: game)
example.save!
end
end
render json: { count: answers.length }, status: :created
end
private
def extra_question_json question
{
id: question.id,
text: question.text,
source: question.source,
priority_weight: question.priority_weight
}
end
def weighted_sample_questions questions, post_id:, limit:
remaining = questions.uniq(&:id)
selected = []
while selected.length < limit && remaining.any?
weighted =
remaining.map { |question|
[question, selection_weight_for(question, post_id: post_id)]
}
total_weight = weighted.sum { |_question, weight| weight }
break if total_weight <= 0
target = rand * total_weight
cumulative = 0.0
chosen =
weighted.find do |_question, weight|
cumulative += weight
cumulative >= target
end&.first || weighted.first.first
selected << chosen
remaining.reject! { |question| question.id == chosen.id }
end
selected
end
def selection_weight_for question, post_id:
sample_count =
question.gekanator_question_examples.sum { |example|
next 0 unless example.post_id == post_id
example.sample_count.presence || 1
}
question.priority_weight.to_f / (1.0 + sample_count * 0.15)
end
end