#1
by
boma
Hi
I’m working on a simple 2-player trading experiment using oTree Studio. I’ve set up a live page where Player 1 makes an offer, and Player 2 receives it in real time via liveRecv and liveSend. I ran it on a demo version. I open the link for both players and Player 1 is indeed able to send an offer and my Anaconda Powershell Prompt does not give me any error message. However, Player 2 never sees the offer update. I used some console demand to figure out the problem and I get this error: Uncaught ReferenceError: liveRecv is not defined.
However, I don't know why this is not working as I included live_method as a player function as suggest in the oTree documentation when using oTree studio. See code below. Do you have any suggestions what the problem is?
Thank you for your help!
Constants
PLAYERS_PER_GROUP = 2
NUM_ROUNDS = 1
ENDOWMENT = cu(100)
Model Player
class Player(BasePlayer):
money_after_trade = models.CurrencyField()
accepted_other_participants_offer = models.BooleanField()
Model Player: Functions
def live_page(player, data):
other_id = 2 if player.id_in_group == 1 else 1
if data['information_type'] == 'offer':
return {
other_id: {
'information_type': 'offer',
'offer': data['offer'],
}
}
elif data['information_type'] == 'response':
return {
other_id: {
'information_type': 'response',
'offer_response': data['offer_response'],
}
}
Page Game
form_fields for player = ['money_after_trade', 'accepted_other_participants_offer']
live_method = 'live_page'
def vars_for_template(player):
return {
'id_in_group': player.id_in_group,
'endowment': C.ENDOWMENT,
}
Still on Page Game (HTML + JavaScript inside Game.html)
<!-- Store group/player data -->
<div id="player-data"
data-id-in-group="{{ id_in_group }}"
data-endowment="{{ endowment }}">
</div>
<!-- Player 1 makes an offer -->
<div id="make_offer" style="display: none;">
You have an endowment of {{ endowment }} <br>
Please make an offer for the good: <br>
<input type="number" id="make_offer_input">
<button type="button" onclick="make_offer()">Make Offer</button>
</div>
<!-- Player 1 waits -->
<div id="wait_for_response" style="display: none;">
Please wait for a response to your offer.
</div>
<!-- Player 2 waits for offer -->
<div id="wait_for_offer" style="display: none;">
Waiting for an offer...
</div>
<!-- Player 2 responds -->
<div id="respond_offer" style="display: none;">
You are offered <span id="current_offer">?</span><br>
<button onclick="accept_offer()">Accept Offer</button>
<button onclick="reject_offer()">Reject Offer</button>
</div>
<!-- Hidden form -->
<form id="form" method="post">
<input type="hidden" id="money_after_trade_input" name="money_after_trade">
<input type="hidden" id="accepted_other_participants_offer_input" name="accepted_other_participants_offer">
</form>
<script>
let current_offer_received;
let current_offer_made;
let has_received_offer = false;
document.addEventListener("DOMContentLoaded", function () {
const data = document.getElementById("player-data").dataset;
const player_id_in_group = parseInt(data.idInGroup);
const endowment = parseFloat(data.endowment);
console.log("Page loaded for Player", player_id_in_group, "| Endowment:", endowment);
if (player_id_in_group === 1) {
console.log("Player 1 sees make_offer");
document.getElementById("make_offer").style.display = "block";
} else {
console.log("Player 2 waiting for offer...");
document.getElementById("wait_for_offer").style.display = "block";
}
// Log that we're setting up liveRecv
console.log("Registering liveRecv() listener...");
liveRecv(function(data) {
console.log("liveRecv triggered with data:", data);
liveRecvData(data, endowment, player_id_in_group);
});
});
function make_offer() {
current_offer_made = parseFloat(document.getElementById("make_offer_input").value);
console.log("Player 1 made offer:", current_offer_made);
liveSend({
"information_type": "offer",
"offer": current_offer_made
});
document.getElementById("make_offer").style.display = "none";
document.getElementById("wait_for_response").style.display = "block";
}
function accept_offer() {
const endowment = parseFloat(document.getElementById("player-data").dataset.endowment);
console.log("Player 2 accepted offer:", current_offer_received);
liveSend({
"information_type": "response",
"offer_response": true
});
const form = document.getElementById("form");
form.money_after_trade.value = endowment - current_offer_received;
form.accepted_other_participants_offer_input.value = '1';
console.log("Submitting form from Player 2 (accept)");
form.submit();
}
function reject_offer() {
const endowment = parseFloat(document.getElementById("player-data").dataset.endowment);
console.log("Player 2 rejected offer:", current_offer_received);
liveSend({
"information_type": "response",
"offer_response": false
});
const form = document.getElementById("form");
form.money_after_trade.value = endowment;
form.accepted_other_participants_offer_input.value = '0';
console.log("Submitting form from Player 2 (reject)");
form.submit();
}
function liveRecvData(data, endowment, player_id_in_group) {
console.log("liveRecvData triggered for player", player_id_in_group, "with:", data);
if (data.information_type === "offer" && player_id_in_group === 2) {
current_offer_received = data.offer;
has_received_offer = true;
console.log("Player 2 received offer:", current_offer_received);
document.getElementById("current_offer").textContent = current_offer_received;
document.getElementById("respond_offer").style.display = "block";
document.getElementById("wait_for_offer").style.display = "none";
} else if (data.information_type === "response" && player_id_in_group === 1) {
console.log("Player 1 received response to their offer.");
const form = document.getElementById("form");
form.money_after_trade.value = endowment - current_offer_made;
form.accepted_other_participants_offer_input.value = data.offer_response ? '1' : '0';
console.log("Submitting form from Player 1");
form.submit();
} else {
console.warn("Received unexpected data or wrong player:", data);
}
}
function pageIsWaiting() {
const player_id_in_group = parseInt(document.getElementById("player-data").dataset.idInGroup);
if (player_id_in_group === 1) return false;
return !has_received_offer;
}
</script>
#2
by
boma
Hello everyone Just a short update concerning my question above. I did not manage to do it with oTree studio, so I switched to "normal" oTree. There the live pages work. So the above problem is solved for me. Thank you!