Need help filling ED fields with Tetris Games scores, results and chat | XM Community
Skip to main content

Hello, 


I’ve been working with some friends to develop the code for an experiment I'm running as part of my Psych Honours year.

The experiment examines the effects of competition level (high vs low) on interpretation of messages in different emotional valences (positive, ambiguous, negative). It then also factors in trait competitiveness, and dark tetrad traits.

Participants play a game of tetris which has been coded in JS and CSS and is hosted on Github.

The Qualtrics questions then create a frame for this game to take place, with survey flow creating two random groups (High or Low competition), with each participant experiencing three different games of Tetris, where after the match, the CPU opponent sends them a valenced message and they can respond.

 

At the moment I have two major issues, and a couple of little ones..

  1. The two major issues are as follows: In the embeddedData fields I’ve created, I can record the message the player sends to the CPU during the chat phase (Embedded as PosChat, NegChat, AmbChat), however I can’t seem to record the player’s Score, and their result (whether they Won, Lost or Tied the game). The 6 fields I have created for this are ‘PosScore, NegScore, AmbScore’ and ‘PosResult, NegResult, AmbResult’, respectively.
  2. Another issue is that sometimes, in the competitive condition, it freezes when the block is at the very top, but this might require some more troubleshooting on my part to work out. It’s only popped up recently, and I haven’t touched the actual script on github so I think it must be something qualtrics related but, I may have missed something.
  3. The frame keeps cutting off parts of my game (Right hand side), however I don’t want to enable a scroll bar, as that makes the user experience unpleasant. I’d rather either A) Shrink the game size to fit the frame, or 😎 Open up the frame further. It feels like Qualtrics has some default padding that I’d love to get rid of.
  4. I’d like users to be able to select ‘Back’, but only once they’ve reached a certain point in the experiment. I don’t want them able to replay the Tetris matches, but I do want them to be able to return to survey questions (trait competitiveness, as well as Dark Tetrad Trait questionnaires) that occur after the experimental aspect has ended.

 

Could anyone help me out?

Thanks in advance!

My code for the Positive Tetris Match is attached. (Ambiguous and Negative are rounds 2 and 3 respectively)

/**
* Qualtrics Embedded Game Script – Tetris Experiment
*
* This script embeds a custom Tetris game into a Qualtrics survey question and manages all logic
* for valence assignment, sequence tracking, gameplay interaction logging, and data collection.
*
* Key functionality:
* - initializes embedded data fields required for storing player and opponent chat
* - tracks sequence round number using embedded data to align responses with round index
* - defines valence-based messaging logic (positive, negative, neutral) mapped by round number
* - constructs and injects the Tetris game iframe with game configuration via URL parameters
* - listens for postMessage events from the game to capture chat, results, and score
* - auto-advances the survey after 30 seconds if no interaction is received
* - supports mobile and desktop layouts based on device detection
*
* The script is hardcoded for round 1 (`FILE_ROUND = 1`) and assumes embedded data is pre-piped.
* Some legacy variables are retained for compatibility but are no longer strictly needed.
*/
Qualtrics.SurveyEngine.addOnReady(function() {
// configuration variables (change these as needed for each round)
const TEST_FIELD = "TestField_Round1"; // used to verify embedded data works
const VALENCE_FIELD = "positive"; // semantic valence condition for this round
const PIPE_SEQ_FIELD = "Seq1"; // embedded data field for piped valence value
const FILE_ROUND = 1; // static file-based round number
const COMPETITION = "${e://Field/Competition}"; // piped embedded competition value
const MODE = "${e://Field/Mode}"; // piped embedded mode value

// fixed valence message sets by sequence round
// the numbers correspond to the file-based round number
var valenceMessages = {
1: {
win: "Thanks for playing. That was great!",
loss: "Thanks for playing. That was great!",
tie: "Thanks for playing. That was great!"
},
2: {
win: "Lol, I beat you! You lost.",
loss: "Lol, whatever. I let you win.",
tie: "Lol, I beat you! You lost."
},
3: {
win: "That was something.",
loss: "That was something.",
tie: "That was something."
}
};

// timeout safeguard: auto-advance after 60 seconds of inactivity
let advanced = false;
const timeoutID = setTimeout(() => {
if (!advanced) {
advanced = true;
jQuery("#NextButton").click();
}
}, 60000);

// force-write a test value into embedded data to confirm data layer is working
Qualtrics.SurveyEngine.setEmbeddedData(TEST_FIELD, "WORKING_" + Date.now());
console.log("FORCE TEST: Created", TEST_FIELD);

// core vars – valence is piped, but we don't use it anymore
// can get rid of this later
var valence = "${e://Field(" + PIPE_SEQ_FIELD + ")}"; // not used
var qid = this.questionId; // current question DOM id

// ensures required embedded data fields for chat logs are present
function initializeChatDataFields() {
for (var i = 1; i <= 3; i++) {
if (Qualtrics.SurveyEngine.getEmbeddedData("ChatResponse" + i) == null)
Qualtrics.SurveyEngine.setEmbeddedData("ChatResponse" + i, "");
if (Qualtrics.SurveyEngine.getEmbeddedData("OpponentChat" + i) == null)
Qualtrics.SurveyEngine.setEmbeddedData("OpponentChat" + i, "");
}
console.log("Initialized ChatResponse1-3 and OpponentChat1-3");
}

// we dont technically need this anymore but dont want to break anything
function getOrSetSequenceRound() {
var cur = Qualtrics.SurveyEngine.getEmbeddedData("CurrentSequence");
if (!cur) {
// on first run, initialize to file round
Qualtrics.SurveyEngine.setEmbeddedData("CurrentSequence", String(FILE_ROUND));
return FILE_ROUND;
}
// on subsequent runs, increment
var next = parseInt(cur, 10) + 1;
Qualtrics.SurveyEngine.setEmbeddedData("CurrentSequence", String(next));
return next;
}

// run setup routines
initializeChatDataFields();
var displayRound = getOrSetSequenceRound(); // used to index into message/field maps
console.log("Display round:", displayRound);

// ensure embedded data for competition and mode are up-to-date
Qualtrics.SurveyEngine.setEmbeddedData("Competition", COMPETITION);
Qualtrics.SurveyEngine.setEmbeddedData("GameMode", MODE);

// resolve the correct valence messages for the current round
var currentValenceMessages = valenceMessagesvdisplayRound] || valenceMessagesn1];
console.log("Valence messages:", currentValenceMessages);

// constructs the iframe src and injects the game into the survey DOM
function startGame(comp, mode) {
// url should not be hardcoded, fix for later
var src = "https://jxmis0n.github.io/TetrisExperiment/"
+ "?competition=" + encodeURIComponent(comp)
+ "&valence=" + encodeURIComponent(VALENCE_FIELD)
+ "&mode=" + encodeURIComponent(mode)
+ "&round=" + displayRound
+ "&winMsg=" + encodeURIComponent(currentValenceMessages.win)
+ "&lossMsg=" + encodeURIComponent(currentValenceMessages.loss)
+ "&tieMsg=" + encodeURIComponent(currentValenceMessages.tie);

// responsive styling based on device type
var isMobile = window.innerWidth <= 480 || /Mobi|Android|iPhone|iPad|iPod/.test(navigator.userAgent);
var style = isMobile
? "width:100%;height:600px;overflow:hidden;margin:0 auto;"
: "width:100%;height:90vh;overflow:hidden;";

var html = '<div style="' + style + '">'
+ '<iframe src="' + src + '" '
+ 'style="width:100%;height:calc(100% - 30px);border:0;" allowfullscreen>'
+ '</iframe>'
+ '</div>';

console.log("Injecting iframe:", src);
jQuery("#" + qid + " .QuestionText").html(html);
}

startGame(COMPETITION, MODE);

// maps to match round index to embedded data field names
var chatFieldMap = { 1: "PosChat", 2: "NegChat", 3: "AmbChat" };
var scoreFieldMap = { 1: "PosScore", 2: "NegScore", 3: "AmbScore" };
var resultFieldMap = { 1: "PosResult", 2: "NegResult", 3: "AmbResult" };

// listens for messages sent from the embedded game
window.addEventListener("message", function(evt) {
var d = evt.data;
if (!d || d.round !== displayRound) return;

// gameEnd message → record result and score
if (d.type === "gameEnd") {
Qualtrics.SurveyEngine.setEmbeddedData(resultFieldMapcdisplayRound], d.result);
Qualtrics.SurveyEngine.setEmbeddedData(scoreFieldMapidisplayRound], d.score);
console.log("Recorded", resultFieldMapbdisplayRound], d.result, scoreFieldMapidisplayRound], d.score);
return;
}

// player chat response → record and auto-advance
if (d.type === "chatResponse") {
var chatField = chatFieldMapedisplayRound];
Qualtrics.SurveyEngine.setEmbeddedData(chatField, d.text);
console.log("Recorded", chatField + ":", d.text);

if (!advanced) {
advanced = true;
clearTimeout(timeoutID);
setTimeout(() => {
jQuery("#NextButton").click();
}, 100);
}
return;
}

// opponent chat → store in round-specific field
if (d.type === "opponentChat") {
Qualtrics.SurveyEngine.setEmbeddedData("OpponentChat" + displayRound, d.text);
console.log("Recorded OpponentChat" + displayRound + ":", d.text);
return;
}
});
});

 

Best,

James.

If it helps, here is the Github link.

The hardcoded.js file is the script for each of the Qualtrics Questions - the round numbers changed to 2 and 3 respectively.

 

https://github.com/jxmis0n/TetrisExperiment


Leave a Reply


OSZAR »