Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "task_intentions",
"version": "1.7.10",
"version": "1.7.11",
"description": "Intentions Game",
"private": true,
"main": "dist/index.js",
Expand Down
10 changes: 8 additions & 2 deletions src/classes/Handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,8 +220,14 @@ class Handler {
// Store social status
const experiment = window.Experiment;
this.dataframe.participantDefaultStatus = experiment.getState().get("participantDefaultStatus");
this.dataframe.partnerLowStatus = experiment.getState().get("partnerLowStatus");
this.dataframe.partnerHighStatus = experiment.getState().get("partnerHighStatus");
this.dataframe.partnerCyberballLowStatus = experiment.getState().get("partnerCyberballLowStatus");
this.dataframe.partnerCyberballHighStatus = experiment.getState().get("partnerCyberballHighStatus");
this.dataframe.partnerOneLowStatus = experiment.getState().get("partnerOneLowStatus");
this.dataframe.partnerOneHighStatus = experiment.getState().get("partnerOneHighStatus");
this.dataframe.partnerTwoLowStatus = experiment.getState().get("partnerTwoLowStatus");
this.dataframe.partnerTwoHighStatus = experiment.getState().get("partnerTwoHighStatus");
this.dataframe.partnerThreeLowStatus = experiment.getState().get("partnerThreeLowStatus");
this.dataframe.partnerThreeHighStatus = experiment.getState().get("partnerThreeHighStatus");

// Finish trial
this.callback();
Expand Down
5 changes: 3 additions & 2 deletions src/classes/factories/ScreenPropFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,8 @@ class ScreenPropFactory implements Factory {
returned.props = {
trial_number: this.trial.trial_number,
display: this.trial.display,
isPartnerHighStatus: this.trial.isPartnerHighStatus,
nextPhase: this.trial.nextPhase,
isPreviewPartnerHighStatus: this.trial.isPreviewPartnerHighStatus,
handler: this.handler.callback.bind(this.handler),
};
break;
Expand Down Expand Up @@ -292,7 +293,7 @@ class ScreenPropFactory implements Factory {
trial_number: this.trial.trial_number,
display: this.trial.display,
isInclusive: this.trial.isInclusive,
partnerHighStatus: this.trial.partnerHighStatus,
isCyberballPartnerHighStatus: this.trial.isCyberballPartnerHighStatus,
probabilities: this.trial.probabilities,
handler: this.handler.cyberball.bind(this.handler),
};
Expand Down
8 changes: 6 additions & 2 deletions src/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,12 @@ export const Configuration = {
signalTimestamps: [],
experimentID: "",
participantDefaultStatus: 0,
partnerHighStatus: 0,
partnerLowStatus: 0,
partnerOneHighStatus: 0,
partnerOneLowStatus: 0,
partnerTwoHighStatus: 0,
partnerTwoLowStatus: 0,
partnerThreeHighStatus: 0,
partnerThreeLowStatus: 0,
},

// Set the logging level
Expand Down
33 changes: 26 additions & 7 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,28 @@ experiment.getState().set("experimentID", experimentID);
initializeLocalStorage(experimentID);

// Generate status values for participant and partner during actual trials
const { participantStatus: participantDefaultStatus, partnerLowStatus, partnerHighStatus } = generateStatuses();
const {
participantDefaultStatus,
partnerCyberballLowStatus,
partnerCyberballHighStatus,
partnerOneLowStatus,
partnerOneHighStatus,
partnerTwoLowStatus,
partnerTwoHighStatus,
partnerThreeLowStatus,
partnerThreeHighStatus,
} = generateStatuses();

// Store status values in the experiment state
experiment.getState().set("participantDefaultStatus", participantDefaultStatus);
experiment.getState().set("partnerLowStatus", partnerLowStatus);
experiment.getState().set("partnerHighStatus", partnerHighStatus);
experiment.getState().set("partnerCyberballLowStatus", partnerCyberballLowStatus);
experiment.getState().set("partnerCyberballHighStatus", partnerCyberballHighStatus);
experiment.getState().set("partnerOneLowStatus", partnerOneLowStatus);
experiment.getState().set("partnerOneHighStatus", partnerOneHighStatus);
experiment.getState().set("partnerTwoLowStatus", partnerTwoLowStatus);
experiment.getState().set("partnerTwoHighStatus", partnerTwoHighStatus);
experiment.getState().set("partnerThreeLowStatus", partnerThreeLowStatus);
experiment.getState().set("partnerThreeHighStatus", partnerThreeHighStatus);

// Setup the Compute instance
window.Compute = new Compute();
Expand Down Expand Up @@ -486,7 +502,7 @@ if (Configuration.manipulations.enableCyberball === true) {
type: Configuration.studyName,
display: "cyberball",
isInclusive: Configuration.manipulations.cyberballIsInclusive,
partnerHighStatus: Configuration.manipulations.cyberballIsPartnerHighStatus,
isCyberballPartnerHighStatus: Configuration.manipulations.cyberballIsPartnerHighStatus,
probabilities: {
inclusion: 0.5,
exclusion: {
Expand Down Expand Up @@ -900,7 +916,8 @@ if (Configuration.manipulations.enableStatusPhaseOne === true) {
timeline.push({
type: Configuration.studyName,
display: "statusPreview",
isPartnerHighStatus: Configuration.manipulations.isPartnerHighStatusPhaseOne,
nextPhase: "phaseOne",
isPreviewPartnerHighStatus: Configuration.manipulations.isPartnerHighStatusPhaseOne,
})
}

Expand Down Expand Up @@ -1275,7 +1292,8 @@ for (let i = 0; i < dataCollection.length; i++) {
timeline.push({
type: Configuration.studyName,
display: "statusPreview",
isPartnerHighStatus: Configuration.manipulations.isPartnerHighStatusPhaseTwo,
nextPhase: "phaseTwo",
isPreviewPartnerHighStatus: Configuration.manipulations.isPartnerHighStatusPhaseTwo,
})
}

Expand Down Expand Up @@ -1467,7 +1485,8 @@ for (let i = 0; i < dataCollection.length; i++) {
timeline.push({
type: Configuration.studyName,
display: "statusPreview",
isPartnerHighStatus: Configuration.manipulations.isPartnerHighStatusPhaseThree,
nextPhase: "phaseThree",
isPreviewPartnerHighStatus: Configuration.manipulations.isPartnerHighStatusPhaseThree,
})
}

Expand Down
46 changes: 40 additions & 6 deletions src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -256,20 +256,54 @@ export const generatePartnerID = (): string => {
* Generate the participant and partner statuses using uniform distributions
* @returns Object containing participant status (40-60%), partner low status (10-25%), and partner high status (75-90%)
*/
export const generateStatuses = (): { participantStatus: number, partnerLowStatus: number, partnerHighStatus: number } => {
export const generateStatuses = (): {
participantDefaultStatus: number;
partnerCyberballLowStatus: number;
partnerCyberballHighStatus: number;
partnerOneLowStatus: number;
partnerOneHighStatus: number;
partnerTwoLowStatus: number;
partnerTwoHighStatus: number;
partnerThreeLowStatus: number;
partnerThreeHighStatus: number;
} => {
// Create uniform distribution generators
const participantRandom = randomUniform(40, 60);
const partnerLowRandom = randomUniform(10, 25);
const partnerHighRandom = randomUniform(75, 90);

// Generate the statuses using uniform distributions
const participantStatus = Math.round(participantRandom() * 10) / 10; // Round to 1 decimal place
const partnerLowStatus = Math.round(partnerLowRandom() * 10) / 10;
const partnerHighStatus = Math.round(partnerHighRandom() * 10) / 10;
const participantDefaultStatus = Math.round(participantRandom() * 10) / 10; // Round to 1 decimal place

// Cyberball partner statuses
const partnerCyberballLowStatus = Math.round(partnerLowRandom() * 10) / 10;
const partnerCyberballHighStatus = Math.round(partnerHighRandom() * 10) / 10;

// Phase One partner statuses
const partnerOneLowStatus = Math.round(partnerLowRandom() * 10) / 10;
const partnerOneHighStatus = Math.round(partnerHighRandom() * 10) / 10;

// Phase Two partner statuses
const partnerTwoLowStatus = Math.round(partnerLowRandom() * 10) / 10;
const partnerTwoHighStatus = Math.round(partnerHighRandom() * 10) / 10;

// Phase Three partner statuses
const partnerThreeLowStatus = Math.round(partnerLowRandom() * 10) / 10;
const partnerThreeHighStatus = Math.round(partnerHighRandom() * 10) / 10;

// Log the statuses
consola.info(`Generated statuses - Participant: ${participantStatus}%, Partner Low: ${partnerLowStatus}%, Partner High: ${partnerHighStatus}%`);
consola.info(`Generated statuses:\nParticipant: ${participantDefaultStatus}%\nPartner Cyberball Low: ${partnerCyberballLowStatus}%, Partner Cyberball High: ${partnerCyberballHighStatus}%\nPartner One Low: ${partnerOneLowStatus}%, Partner One High: ${partnerOneHighStatus}%\nPartner Two Low: ${partnerTwoLowStatus}%, Partner Two High: ${partnerTwoHighStatus}%\nPartner Three Low: ${partnerThreeLowStatus}%, Partner Three High: ${partnerThreeHighStatus}%`);

// Return all three statuses
return { participantStatus, partnerLowStatus, partnerHighStatus };
return {
participantDefaultStatus,
partnerCyberballLowStatus,
partnerCyberballHighStatus,
partnerOneLowStatus,
partnerOneHighStatus,
partnerTwoLowStatus,
partnerTwoHighStatus,
partnerThreeLowStatus,
partnerThreeHighStatus,
};
};
3 changes: 2 additions & 1 deletion src/view/components/Card/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ const Card: FC<Components.Card> = (
id="playerInfo"
margin={{ left: "small", right: "small" }}
align="center"
justify="center"
>
<Heading level={1}>{props.name}</Heading>

Expand All @@ -59,7 +60,7 @@ const Card: FC<Components.Card> = (
</Box>

<Heading level={1}>
<Box direction="row" gap="xsmall">
<Box direction="row" gap="xsmall" height="64px">
<Money size="large" color="pointsIconBackground" />
<TextTransition
text={props.points}
Expand Down
3 changes: 2 additions & 1 deletion src/view/screens/Cyberball/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ const Cyberball: FC<Screens.Cyberball> = (

// Generate the strings to determine the participant and partner statuses
const participantStatus = experiment.getState().get("participantDefaultStatus");
const partnerStatus = props.partnerHighStatus ? experiment.getState().get("partnerHighStatus") : experiment.getState().get("partnerLowStatus");
const partnerStatus = props.isCyberballPartnerHighStatus ? experiment.getState().get("partnerCyberballHighStatus") : experiment.getState().get("partnerCyberballLowStatus");

// Force re-render mechanism
const [, forceUpdate] = useReducer(x => x + 1, 0);
Expand Down Expand Up @@ -243,6 +243,7 @@ const Cyberball: FC<Screens.Cyberball> = (
position: "absolute",
top: "-70px",
left: "50%",
width: "100%",
transform: "translateX(-50%)",
zIndex: 1000,
}}
Expand Down
13 changes: 12 additions & 1 deletion src/view/screens/StatusPreview/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,18 @@ import { Configuration } from "src/configuration";
const StatusPreview: FC<Screens.StatusPreview> = (props: Screens.StatusPreview): ReactElement => {
// Get the current partner avatar and social standing
const experiment = window.Experiment;
const partnerStatus = props.isPartnerHighStatus ? experiment.getState().get("partnerHighStatus") : experiment.getState().get("partnerLowStatus");

// Determine the partner status
let partnerStatus = 0;
if (props.nextPhase === "cyberball") {
partnerStatus = props.isPreviewPartnerHighStatus ? experiment.getState().get("partnerCyberballHighStatus") : experiment.getState().get("partnerCyberballLowStatus");
} else if (props.nextPhase === "phaseOne") {
partnerStatus = props.isPreviewPartnerHighStatus ? experiment.getState().get("partnerOneHighStatus") : experiment.getState().get("partnerOneLowStatus");
} else if (props.nextPhase === "phaseTwo") {
partnerStatus = props.isPreviewPartnerHighStatus ? experiment.getState().get("partnerTwoHighStatus") : experiment.getState().get("partnerTwoLowStatus");
} else if (props.nextPhase === "phaseThree") {
partnerStatus = props.isPreviewPartnerHighStatus ? experiment.getState().get("partnerThreeHighStatus") : experiment.getState().get("partnerThreeLowStatus");
}

// Get the updated partner avatar
const partnerAvatar = Configuration.avatars.names.partner[
Expand Down
23 changes: 7 additions & 16 deletions src/view/screens/Trial/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -193,27 +193,18 @@ const Trial: FC<Screens.Trial> = (

// Initialize participant and partner statuses
const participantStatus = experiment.getState().get("participantDefaultStatus"); // Default for all trials
let partnerStatus = experiment.getState().get("partnerLowStatus");
let partnerStatus = experiment.getState().get("partnerCyberballLowStatus"); // Default to the Cyberball low status
if (Flags.isEnabled("enableStatusDisplay") && props.isPractice === false) {
// If status display is enabled, interpret and apply the manipulations
if (
props.display === "playerChoice" &&
Configuration.manipulations.isPartnerHighStatusPhaseOne
) {
if (props.display === "playerChoice") {
// Phase One: If `isHighStatusPhaseOne` is true, then the participant is low status and the partner is high status
partnerStatus = experiment.getState().get("partnerHighStatus");
} else if (
props.display === "playerGuess" &&
Configuration.manipulations.isPartnerHighStatusPhaseTwo
) {
partnerStatus = Configuration.manipulations.isPartnerHighStatusPhaseOne ? experiment.getState().get("partnerOneHighStatus") : experiment.getState().get("partnerOneLowStatus");
} else if (props.display === "playerGuess") {
// Phase Two: If `isHighStatusPhaseTwo` is true, then the participant is low status and the partner is high status
partnerStatus = experiment.getState().get("partnerHighStatus");
} else if (
props.display === "playerChoice2" &&
Configuration.manipulations.isPartnerHighStatusPhaseThree
) {
partnerStatus = Configuration.manipulations.isPartnerHighStatusPhaseTwo ? experiment.getState().get("partnerTwoHighStatus") : experiment.getState().get("partnerTwoLowStatus");
} else if (props.display === "playerChoice2") {
// Phase Three: If `isHighStatusPhaseThree` is true, then the participant is low status and the partner is high status
partnerStatus = experiment.getState().get("partnerHighStatus");
partnerStatus = Configuration.manipulations.isPartnerHighStatusPhaseThree ? experiment.getState().get("partnerThreeHighStatus") : experiment.getState().get("partnerThreeLowStatus");
}
}

Expand Down
10 changes: 8 additions & 2 deletions test/utils/functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,14 @@ export const getHandler = (display: Display): Handler => {
playerPoints_selected: NaN,
partnerPoints_selected: NaN,
participantDefaultStatus: 0,
partnerLowStatus: 0,
partnerHighStatus: 0,
partnerCyberballLowStatus: 0,
partnerCyberballHighStatus: 0,
partnerOneLowStatus: 0,
partnerOneHighStatus: 0,
partnerTwoLowStatus: 0,
partnerTwoHighStatus: 0,
partnerThreeLowStatus: 0,
partnerThreeHighStatus: 0,
selectedOption_player: NaN,
realAnswer: "Option 1",
questionnaireResponseInferenceSelfish: NaN,
Expand Down
8 changes: 4 additions & 4 deletions test/view/screens/Cyberball.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ describe("Cyberball Screen", () => {
trial_number: 1,
display: "cyberball" as const,
isInclusive: true,
partnerHighStatus: true,
isCyberballPartnerHighStatus: true,
probabilities: {
inclusion: 0.7,
exclusion: {
Expand Down Expand Up @@ -176,17 +176,17 @@ describe("Cyberball Screen", () => {
expect(screen.getByText("High")).toBeInTheDocument();
});

it("shows correct status positioning based on partnerHighStatus prop", () => {
it("shows correct status positioning based on isCyberballPartnerHighStatus prop", () => {
// Test with high status partner
const highStatusProps = { ...mockProps, partnerHighStatus: true };
const highStatusProps = { ...mockProps, isCyberballPartnerHighStatus: true };
const { rerender } = render(<Cyberball {...highStatusProps} />);

// Check that the status label is present
expect(screen.getByText("Low")).toBeInTheDocument();
expect(screen.getByText("High")).toBeInTheDocument();

// Test with low status partner
const lowStatusProps = { ...mockProps, partnerHighStatus: false };
const lowStatusProps = { ...mockProps, isCyberballPartnerHighStatus: false };
rerender(<Cyberball {...lowStatusProps} />);

// Check that the status label is still present
Expand Down
10 changes: 8 additions & 2 deletions types/data.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,14 @@ declare type Dataframe = {

// Social status
participantDefaultStatus: number;
partnerLowStatus: number;
partnerHighStatus: number;
partnerCyberballLowStatus: number;
partnerCyberballHighStatus: number;
partnerOneLowStatus: number;
partnerOneHighStatus: number;
partnerTwoLowStatus: number;
partnerTwoHighStatus: number;
partnerThreeLowStatus: number;
partnerThreeHighStatus: number;

// Signal timestamps
signalTimestamps: number[];
Expand Down
5 changes: 3 additions & 2 deletions types/jsPsych.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ declare type IntentionsNode = {
runComputeOperation?: boolean; // Whether to fetch data from server (only used when state is "matchingIntentions")

// Status preview screen configuration (used by StatusPreview screen)
isPartnerHighStatus?: boolean; // Used for the `StatusPreview` screen
nextPhase?: "cyberball" | "phaseOne" | "phaseTwo" | "phaseThree";
isPreviewPartnerHighStatus?: boolean; // Used for the `StatusPreview` screen

// Waiting screen configuration (used by Waiting screen)
mode: "facilitator" | "mri";
Expand All @@ -96,7 +97,7 @@ declare type IntentionsNode = {

// Cyberball screen
isInclusive?: boolean;
partnerHighStatus?: boolean;
isCyberballPartnerHighStatus?: boolean;
probabilities?: {
inclusion: number;
exclusion: {
Expand Down
5 changes: 3 additions & 2 deletions types/props.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,8 @@ declare namespace Screens {

// Status preview screen
type StatusPreview = GenericScreenProps & {
isPartnerHighStatus: boolean;
nextPhase: "cyberball" | "phaseOne" | "phaseTwo" | "phaseThree";
isPreviewPartnerHighStatus: boolean;
handler: () => void;
};

Expand Down Expand Up @@ -199,7 +200,7 @@ declare namespace Screens {
// Cyberball screen
type Cyberball = GenericScreenProps & {
isInclusive: boolean; // Operate exclusively as inclusive or exclusive
partnerHighStatus: boolean; // Partner A is high status or low status
isCyberballPartnerHighStatus: boolean; // Partner A is high status or low status
probabilities: {
inclusion: number; // Probability of partners passing to participant
exclusion: {
Expand Down