library(qualtRics)
library(psych)
library(scales)
library(ggplot2)
library(knitr)
library(kableExtra)
library(interactions) # for interact_plot()
library(mediation) # for mediation analysis
library(gridExtra)
library(tidyverse)
Key Takeaways — Pilot 4
1. Narcissism is the strongest individual difference finding
in the study, though this should be interpreted cautiously.
High narcissists aggressed significantly more in easy mode but pulled
back in hard mode (Narcissism × Condition → Total Aggression: b = −3.04,
p = .004), and showed more guilt specifically when winning (Narcissism ×
Condition → Felt Guilt: b = −0.61, p < .001). This suggests
narcissists aggress opportunistically — maximizing dominance when they
have the upper hand — and experience some form of self-focused
discomfort when doing so, rather than empathy-based guilt. That said,
narcissism was measured as a single item and its placement in the survey
flow (after playing the game) may have primed certain response patterns,
so these effects should prob be replicated. Narcissism was also very low
in our sample.
2. Stuns and raids dissociate by condition, suggesting two
distinct forms of aggression. Easy mode players stunned more (M
= 7.86 vs 6.17, p = .025) while hard mode players raided more (M = 3.67
vs 2.46, p = .001). Stunning appears to be offensive, power-driven
aggression available only when winning; raiding may reflect a
desperation resource strategy when losing. This distinction was absent
in Pilot 3, where stuns showed no condition difference at all.
3. Guilt follows power, not perception of opponents.
Easy mode players felt significantly more post-game guilt than hard mode
players (M = 1.88 vs 1.46, p = .006) — the opposite direction from what
a frustration-aggression account would predict — because easy mode
players were the ones doing the aggressing. Believing you were playing
real people (vs. bots), even with high confidence, did not predict felt
guilt, and GP did not moderate this relationship. The
guilt-as-moral-brake hypothesis is not supported in this paradigm.
DANG!!!!
4. Guilt proneness fails to replicate as a
moderator. In Pilot 3, GP showed a trending interaction with
condition on intent to aggress (p = .083), suggesting low GP people were
more willing to attack when winning. In Pilot 4, the same interaction is
essentially zero across every outcome — aggression, stuns, raids, felt
guilt, and moral outrage (all p > .38).
5. Hard mode players are consistently more negative in affect
and less willing to re-engage. Condition predicted
disappointment (hard > easy), moral outrage (hard > easy), and
desire to play again (easy M = 3.90 vs hard M = 2.28, b = −1.62, p <
.001, R² = .15) — the largest effect size in the study. Neither felt
guilt nor GP explained the play-again effect; it is driven entirely by
the experience of losing. The play-again condition gap widened from
Pilot 3 (Δ = 1.25) to Pilot 4 (Δ = 1.62).
1. Data Import & Cleaning
1.1 Load Data
# Read raw Qualtrics export (skip the two label/importId rows)
raw <- read_survey("~/Google drive/My Drive/YEAR 3/PROJECTS/DANIEL/Competitive Jungle/CWV x Game/pilot4_data.csv")
cat("Raw N =", nrow(raw), "\n")
## Raw N = 322
cat("Condition split (raw):\n")
## Condition split (raw):
print(table(raw$cond))
##
## easy hard
## 157 158
1.2 Apply Exclusions
# Read exclusion list
exclusions <- read_csv("~/Downloads/Pilot 4 List of Exclusions (260630) - Sheet1.csv")
cat("Raw N =", nrow(raw), "\n")
## Raw N = 322
cat("Exclusions N =", nrow(exclusions), "\n")
## Exclusions N = 57
## Note: there are a lot of exclusions. People wrote in free response that they did not understand the game, many players made no moves, and some left the game early. All were excluded.
df <- raw %>%
filter(!participantId %in% exclusions$ID) %>%
filter(Finished == 1) %>%
filter(Q_RecaptchaScore > 0.5)
cat("N after exclusions =", nrow(df), "\n")
## N after exclusions = 246
cat("Condition split:\n")
## Condition split:
print(table(df$cond))
##
## easy hard
## 129 117
1.3 Reverse-Score & Compute Composites
df <- df %>%
mutate(
# ── Condition ─────────────────────────────────────────────────────────────
cond = factor(cond, levels = c("easy", "hard")),
cond_num = if_else(cond == "hard", 1, 0), # hard = 1, easy = 0
# ── TIPI reverse scores ──────────────────────────────────────────────────
Extraversion_6R_r = 8 - Extraversion_6R,
Agreeable_2R_r = 8 - Agreeable_2R,
Conscientious_8R_r = 8 - Conscientious_8R,
EmoStability_4R_r = 8 - EmoStability_4R,
Open_10R_r = 8 - Open_10R,
# ── TIPI composites ──────────────────────────────────────────────────────
tipi_extraversion = (Extraversion_1 + Extraversion_6R_r) / 2,
tipi_agreeableness = (Agreeable_7 + Agreeable_2R_r) / 2,
tipi_conscientiousness = (Conscientious_3 + Conscientious_8R_r) / 2,
tipi_emo_stability = (EmoStability_9 + EmoStability_4R_r) / 2,
tipi_openness = (Open_5 + Open_10R_r) / 2,
# ── GP-5 composite (1–5 scale) ───────────────────────────────────────────
gp = rowMeans(pick(GP_1, GP_2, GP_3, GP_4, GP_5), na.rm = TRUE),
gp_c = as.numeric(scale(gp)),
# ── Everyday Sadism composite (1–7 scale) ────────────────────────────────
sadism = rowMeans(pick(sadism_1, sadism_2, sadism_3, sadism_4,
sadism_5, sadism_6, sadism_7, sadism_8),
na.rm = TRUE),
sadism_c = as.numeric(scale(sadism)),
# ── Self-esteem & Narcissism (single items, 1–7) ─────────────────────────
selfesteem_c = as.numeric(scale(selfesteem)),
narcissism_c = as.numeric(scale(narcissism)),
# ── Moral outrage composite ──────────────────────────────────────────────
moral_outrage = rowMeans(pick(outrage_1, outrage_2, outrage_3), na.rm = TRUE),
# ── Subjective behavioral intent ─────────────────────────────────────────
# Aggression = mean of intent to steal and intent to stun
intent_aggression = rowMeans(pick(intent_steal, intent_stun), na.rm = TRUE),
# ── Behavioral aggression composites ─────────────────────────────────────
total_stuns = rowSums(
pick(Player_stuns_Attacker, Player_stuns_Thief, Player_stuns_Freerider),
na.rm = TRUE
),
total_aggression = total_stuns + Player_raids_hut,
log_aggression = log(total_aggression + 1),
# ── People or Bot ────────────────────────────────────────────────────────
# 1 = Real people, 2 = Computer characters, 3 = Unsure
people_or_bot_label = case_when(
people_or_bot == 1 ~ "Real people",
people_or_bot == 2 ~ "Computer/bots",
people_or_bot == 3 ~ "Unsure",
TRUE ~ NA_character_
),
people_or_bot_label = factor(
people_or_bot_label,
levels = c("Real people", "Unsure", "Computer/bots")
),
# Dummy: TRUE if participant believed opponents were real humans
believed_real = (people_or_bot == 1),
# Continuous humanness: recode so higher = more human (flip scale)
humanness = case_when(
people_or_bot == 1 ~ 2,
people_or_bot == 3 ~ 1,
people_or_bot == 2 ~ 0,
TRUE ~ NA_real_
),
# ── Demographics ─────────────────────────────────────────────────────────
gender_label = case_when(
gender == 1 ~ "Male",
gender == 2 ~ "Female",
gender == 3 ~ "Non-binary",
TRUE ~ "Other/NR"
),
game_freq_label = case_when(
game_frequency == 1 ~ "Never",
game_frequency == 2 ~ "< Once/month",
game_frequency == 3 ~ "Few times/month",
game_frequency == 4 ~ "Few times/week",
game_frequency == 5 ~ "Daily/almost daily",
TRUE ~ NA_character_
),
game_freq_label = factor(
game_freq_label,
levels = c("Never", "< Once/month", "Few times/month",
"Few times/week", "Daily/almost daily")
)
)
2. Demographics & Gaming Experience
2.1 Sample Overview
demo_summary <- df %>%
summarise(
N = n(),
Age_M = round(mean(as.numeric(age), na.rm = TRUE), 1),
Age_SD = round(sd(as.numeric(age), na.rm = TRUE), 1),
Age_Range = paste0(min(as.numeric(age), na.rm = TRUE), "–",
max(as.numeric(age), na.rm = TRUE)),
Pct_Female = paste0(round(mean(gender == 2, na.rm = TRUE) * 100, 1), "%"),
Pct_Male = paste0(round(mean(gender == 1, na.rm = TRUE) * 100, 1), "%"),
Pct_NonBin = paste0(round(mean(gender == 3, na.rm = TRUE) * 100, 1), "%")
)
kable(demo_summary,
col.names = c("N", "Age M", "Age SD", "Age Range",
"% Female", "% Male", "% Non-binary"),
caption = "Sample demographics") %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Sample demographics
|
N
|
Age M
|
Age SD
|
Age Range
|
% Female
|
% Male
|
% Non-binary
|
|
246
|
41.8
|
12.2
|
19–78
|
54.9%
|
43.9%
|
1.2%
|
2.2 Gender Distribution
df %>%
count(gender_label) %>%
mutate(pct = n / sum(n)) %>%
ggplot(aes(x = reorder(gender_label, -n), y = pct, fill = gender_label)) +
geom_col(show.legend = FALSE) +
geom_text(aes(label = paste0(n, "\n(", percent(pct, 1), ")")),
vjust = -0.3, size = 3.5) +
scale_y_continuous(labels = percent_format(), limits = c(0, .7)) +
scale_fill_brewer(palette = "Set2") +
labs(title = "Gender Distribution", x = NULL, y = "Proportion") +
theme_minimal()

2.3 Race/Ethnicity
race_labels <- c(
"1" = "White/Eur. Am.", "2" = "Black/Afr. Am.",
"3" = "E. Asian/Am.", "4" = "S. Asian/Am.",
"5" = "Latino/Hisp.", "6" = "Native Am.",
"7" = "Middle Eastern", "8" = "Biracial/Multi.",
"9" = "Other"
)
df %>%
mutate(race_label = race_labels[as.character(race)]) %>%
count(race_label) %>%
arrange(desc(n)) %>%
mutate(pct = percent(n / sum(n), 1)) %>%
kable(col.names = c("Race/Ethnicity", "N", "%"),
caption = "Race/ethnicity breakdown") %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Race/ethnicity breakdown
|
Race/Ethnicity
|
N
|
%
|
|
White/Eur. Am.
|
177
|
72%
|
|
Black/Afr. Am.
|
25
|
10%
|
|
Latino/Hisp.
|
13
|
5%
|
|
NA
|
11
|
4%
|
|
E. Asian/Am.
|
8
|
3%
|
|
Biracial/Multi.
|
7
|
3%
|
|
S. Asian/Am.
|
2
|
1%
|
|
Middle Eastern
|
1
|
0%
|
|
Native Am.
|
1
|
0%
|
|
Other
|
1
|
0%
|
2.4 Education & SES
edu_labels <- c(
"1" = "Some HS", "2" = "HS", "3" = "Some College",
"4" = "College", "5" = "Some Grad", "6" = "MA",
"7" = "PhD", "8" = "MD", "9" = "MBA",
"10" = "JD", "11" = "Other"
)
p_edu <- df %>%
mutate(edu_label = edu_labels[as.character(edu)]) %>%
count(edu_label) %>%
mutate(edu_label = fct_reorder(edu_label, n)) %>%
ggplot(aes(x = edu_label, y = n)) +
geom_col(fill = "#5B8DB8") +
coord_flip() +
labs(title = "Education", x = NULL, y = "N") +
theme_minimal()
ses_labels <- c(
"1" = "Upper", "2" = "Upper Middle", "3" = "Middle",
"4" = "Lower Middle", "5" = "Working", "6" = "Lower"
)
p_ses <- df %>%
mutate(ses_label = ses_labels[as.character(ses)]) %>%
count(ses_label) %>%
mutate(ses_label = fct_reorder(ses_label, n)) %>%
ggplot(aes(x = ses_label, y = n)) +
geom_col(fill = "#E07B54") +
coord_flip() +
labs(title = "Socioeconomic Status", x = NULL, y = "N") +
theme_minimal()
grid.arrange(p_edu, p_ses, ncol = 2)

2.5 Gaming Experience
p_freq <- df %>%
filter(!is.na(game_freq_label)) %>%
count(game_freq_label) %>%
ggplot(aes(x = game_freq_label, y = n, fill = game_freq_label)) +
geom_col(show.legend = FALSE) +
geom_text(aes(label = n), vjust = -0.3, size = 3.5) +
scale_fill_brewer(palette = "Blues", direction = 1) +
labs(title = "Gaming Frequency", x = NULL, y = "N") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 30, hjust = 1))
p_skill <- df %>%
count(skill_level) %>%
ggplot(aes(x = factor(skill_level), y = n)) +
geom_col(fill = "#6BAE75") +
scale_x_discrete(labels = c("Far below\navg", "Below\navg", "Slightly\nbelow",
"Average", "Slightly\nabove", "Above\navg",
"Far above\navg")) +
labs(title = "Self-Rated Skill Level", x = NULL, y = "N") +
theme_minimal()
grid.arrange(p_freq, p_skill, ncol = 2)

3. Scale Reliability
alphas <- list(
"Moral Outrage" = df[, c("outrage_1", "outrage_2", "outrage_3")],
"GP-5" = df[, c("GP_1", "GP_2", "GP_3", "GP_4", "GP_5")],
"Sadism (BCTS)" = df[, c("sadism_1", "sadism_2", "sadism_3", "sadism_4",
"sadism_5", "sadism_6", "sadism_7", "sadism_8")]
)
alpha_table <- map_dfr(alphas, function(items) {
a <- psych::alpha(items, warnings = FALSE)
tibble(alpha = round(a$total$raw_alpha, 3), n_items = ncol(items))
}, .id = "Scale")
kable(alpha_table,
col.names = c("Scale", "Cronbach's α", "N Items"),
caption = "Internal consistency of composites") %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Internal consistency of composites
|
Scale
|
Cronbach’s α
|
N Items
|
|
Moral Outrage
|
0.916
|
3
|
|
GP-5
|
0.771
|
5
|
|
Sadism (BCTS)
|
0.898
|
8
|
Note: Self-esteem and narcissism are single items
and do not have reliability estimates.
4. Descriptives by Condition
desc_vars <- c("moral_outrage", "surprised", "disappointed", "felt_guilt",
"selfesteem", "narcissism", "sadism", "gp",
"people_or_bot", "confidence",
"Player_score", "total_stuns", "total_aggression", "intent_aggression", "Player_raids_hut",
"Player_produces_fruit", "difficulty",
"positive", "play_again")
desc_table <- df %>%
group_by(cond) %>%
summarise(across(
all_of(desc_vars),
list(M = ~round(mean(as.numeric(.x), na.rm = TRUE), 2),
SD = ~round(sd(as.numeric(.x), na.rm = TRUE), 2)),
.names = "{.col}_{.fn}"
)) %>%
pivot_longer(-cond, names_to = c("Variable", "stat"), names_sep = "_(?=[MS])") %>%
pivot_wider(names_from = c(cond, stat), values_from = value)
desc_table <- desc_table[, c("Variable", "easy_M", "easy_SD", "hard_M", "hard_SD")]
kable(desc_table,
col.names = c("Variable", "Easy M", "Easy SD", "Hard M", "Hard SD"),
caption = "Descriptive statistics by condition") %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Descriptive statistics by condition
|
Variable
|
Easy M
|
Easy SD
|
Hard M
|
Hard SD
|
|
moral_outrage
|
2.57
|
1.85
|
3.46
|
1.95
|
|
surprised
|
2.93
|
1.93
|
3.52
|
2.20
|
|
disappointed
|
3.00
|
2.17
|
4.39
|
2.13
|
|
felt_guilt
|
1.88
|
1.31
|
1.46
|
0.97
|
|
selfesteem
|
4.60
|
1.67
|
4.27
|
1.64
|
|
narcissism
|
1.43
|
0.74
|
1.77
|
1.34
|
|
sadism
|
1.54
|
0.80
|
1.73
|
0.93
|
|
gp
|
4.00
|
0.83
|
3.98
|
0.69
|
|
people_or_bot
|
1.90
|
0.62
|
1.96
|
0.56
|
|
confidence
|
4.67
|
1.77
|
5.27
|
1.71
|
|
Player_score
|
3.91
|
2.85
|
0.31
|
0.53
|
|
total_stuns
|
7.86
|
6.93
|
6.17
|
4.73
|
|
total_aggression
|
10.32
|
7.68
|
9.84
|
6.39
|
|
intent_aggression
|
4.35
|
1.56
|
3.83
|
1.56
|
|
Player_raids_hut
|
2.46
|
2.23
|
3.67
|
3.27
|
|
Player_produces_fruit
|
3.95
|
2.09
|
3.68
|
2.30
|
|
difficulty
|
4.04
|
1.93
|
6.40
|
0.91
|
|
positive
|
3.32
|
1.20
|
2.07
|
0.94
|
|
play_again
|
3.90
|
2.12
|
2.28
|
1.69
|
4.1 Trait Distributions
df %>%
dplyr::select(gp, sadism, selfesteem, narcissism) %>%
pivot_longer(everything(), names_to = "Trait", values_to = "Score") %>%
mutate(Trait = recode(Trait,
"gp" = "Guilt Proneness (1–5)",
"sadism" = "Everyday Sadism (1–7)",
"selfesteem" = "Self-Esteem (1–7)",
"narcissism" = "Narcissism (1–7)")) %>%
ggplot(aes(x = as.numeric(Score))) +
geom_histogram(bins = 20, fill = "#5B8DB8", color = "white") +
facet_wrap(~Trait, scales = "free") +
labs(title = "Distribution of Traits", x = "Score", y = "Count") +
theme_minimal()

mean(df$gp)
## [1] 3.99187
sd(df$gp)
## [1] 0.7649029
5. Manipulation Checks
5.1 Perceived Difficulty
t_diff <- t.test(as.numeric(difficulty) ~ cond, data = df)
print(t_diff)
##
## Welch Two Sample t-test
##
## data: as.numeric(difficulty) by cond
## t = -12.482, df = 186.34, p-value < 2.2e-16
## alternative hypothesis: true difference in means between group easy and group hard is not equal to 0
## 95 percent confidence interval:
## -2.736412 -1.989488
## sample estimates:
## mean in group easy mean in group hard
## 4.038760 6.401709
df %>%
group_by(cond) %>%
summarise(M = mean(as.numeric(difficulty), na.rm = TRUE),
SE = sd(as.numeric(difficulty), na.rm = TRUE) / sqrt(n())) %>%
ggplot(aes(x = cond, y = M, fill = cond)) +
geom_col(width = 0.5, show.legend = FALSE) +
geom_errorbar(aes(ymin = M - SE, ymax = M + SE), width = 0.15) +
scale_fill_manual(values = c("easy" = "#5B8DB8", "hard" = "#E07B54")) +
scale_y_continuous(limits = c(0, 7)) +
labs(title = "Perceived Difficulty by Condition",
x = "Condition", y = "Mean Difficulty (1–7)") +
theme_minimal()

5.2 Player Score
t_score <- t.test(as.numeric(Player_score) ~ cond, data = df)
print(t_score)
##
## Welch Two Sample t-test
##
## data: as.numeric(Player_score) by cond
## t = 14.067, df = 137.82, p-value < 2.2e-16
## alternative hypothesis: true difference in means between group easy and group hard is not equal to 0
## 95 percent confidence interval:
## 3.093368 4.105201
## sample estimates:
## mean in group easy mean in group hard
## 3.9069767 0.3076923
df %>%
group_by(cond) %>%
summarise(M = mean(as.numeric(Player_score), na.rm = TRUE),
SE = sd(as.numeric(Player_score), na.rm = TRUE) / sqrt(n())) %>%
ggplot(aes(x = cond, y = M, fill = cond)) +
geom_col(width = 0.5, show.legend = FALSE) +
geom_errorbar(aes(ymin = M - SE, ymax = M + SE), width = 0.15) +
scale_fill_manual(values = c("easy" = "#5B8DB8", "hard" = "#E07B54")) +
labs(title = "Player Score by Condition",
x = "Condition", y = "Mean Score") +
theme_minimal()

6. Distribution of Aggression
summary(df$total_aggression)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.00 5.00 9.00 10.09 14.00 49.00
ggplot(df, aes(x = total_aggression)) +
geom_histogram(bins = 30, fill = "#5B8DB8", color = "white") +
labs(title = "Distribution of Total Aggression",
x = "Total Aggression (stuns + raids)", y = "Count") +
theme_minimal()

ggplot(df, aes(x = total_aggression, fill = cond)) +
geom_histogram(bins = 30, alpha = 0.7, position = "identity") +
scale_fill_manual(values = c("easy" = "#5B8DB8", "hard" = "#E07B54"),
name = "Condition") +
facet_wrap(~cond) +
labs(title = "Distribution of Total Aggression by Condition",
x = "Total Aggression", y = "Count") +
theme_minimal()

cat("Proportion of zeros:", round(mean(df$total_aggression == 0, na.rm = TRUE), 3), "\n")
## Proportion of zeros: 0.065
cat("Max:", max(df$total_aggression, na.rm = TRUE), "\n")
## Max: 49
cat("Mean:", round(mean(df$total_aggression, na.rm = TRUE), 2), "\n")
## Mean: 10.09
cat("Variance/Mean ratio:", round(var(df$total_aggression, na.rm = TRUE) /
mean(df$total_aggression, na.rm = TRUE), 2),
"(>1 = overdispersion)\n")
## Variance/Mean ratio: 4.98 (>1 = overdispersion)
ggplot(df, aes(x = log_aggression)) +
geom_histogram(bins = 30, fill = "#6BAE75", color = "white") +
labs(title = "Distribution of Log(Total Aggression + 1)",
x = "Log Aggression", y = "Count") +
theme_minimal()

7. Correlations
7.1 Overall Correlation Matrix
cor_vars <- df %>%
dplyr::select(
moral_outrage, surprised, disappointed, felt_guilt,
gp, sadism, selfesteem, narcissism,
tipi_agreeableness, tipi_openness,
people_or_bot, confidence,
Player_score, total_stuns, Player_raids_hut,
Player_produces_fruit, intent_aggression,
difficulty, positive, play_again
) %>%
mutate(across(everything(), as.numeric))
names(cor_vars) <- c(
"Moral Outrage", "Surprised", "Disappointed", "Felt Guilt",
"Guilt Proneness", "Sadism", "Self-Esteem", "Narcissism",
"Agreeableness", "Openness",
"People/Bot", "Confidence",
"Score", "Stuns", "Raids", "Produces Fruit", "Intent Aggress",
"Difficulty", "Positive", "Play Again"
)
cor(cor_vars, use = "pairwise.complete.obs") %>%
round(2) %>%
kable(caption = "Pairwise correlations among key variables") %>%
kable_styling(bootstrap_options = c("striped", "condensed"),
font_size = 10, full_width = TRUE) %>%
scroll_box(width = "100%")
Pairwise correlations among key variables
|
|
Moral Outrage
|
Surprised
|
Disappointed
|
Felt Guilt
|
Guilt Proneness
|
Sadism
|
Self-Esteem
|
Narcissism
|
Agreeableness
|
Openness
|
People/Bot
|
Confidence
|
Score
|
Stuns
|
Raids
|
Produces Fruit
|
Intent Aggress
|
Difficulty
|
Positive
|
Play Again
|
|
Moral Outrage
|
1.00
|
0.56
|
0.80
|
0.09
|
0.07
|
0.03
|
-0.03
|
0.16
|
0.03
|
-0.09
|
0.07
|
0.14
|
-0.26
|
-0.10
|
0.02
|
0.09
|
-0.07
|
0.30
|
-0.47
|
-0.35
|
|
Surprised
|
0.56
|
1.00
|
0.63
|
0.09
|
0.15
|
0.01
|
0.05
|
0.10
|
0.09
|
-0.03
|
-0.07
|
0.00
|
-0.15
|
-0.11
|
0.00
|
0.09
|
-0.10
|
0.22
|
-0.24
|
-0.10
|
|
Disappointed
|
0.80
|
0.63
|
1.00
|
0.07
|
0.07
|
0.02
|
-0.05
|
0.14
|
0.03
|
-0.07
|
0.04
|
0.08
|
-0.30
|
-0.12
|
0.08
|
0.08
|
-0.12
|
0.36
|
-0.49
|
-0.34
|
|
Felt Guilt
|
0.09
|
0.09
|
0.07
|
1.00
|
0.11
|
0.09
|
-0.06
|
0.17
|
-0.03
|
-0.03
|
0.08
|
-0.02
|
0.11
|
0.11
|
-0.08
|
-0.05
|
0.01
|
-0.09
|
-0.04
|
0.02
|
|
Guilt Proneness
|
0.07
|
0.15
|
0.07
|
0.11
|
1.00
|
-0.31
|
0.16
|
-0.07
|
0.35
|
0.03
|
0.00
|
0.08
|
-0.05
|
-0.09
|
-0.03
|
-0.04
|
-0.07
|
0.09
|
0.01
|
0.01
|
|
Sadism
|
0.03
|
0.01
|
0.02
|
0.09
|
-0.31
|
1.00
|
-0.10
|
0.43
|
-0.49
|
-0.11
|
0.00
|
0.05
|
-0.01
|
0.01
|
-0.07
|
0.11
|
0.04
|
-0.02
|
0.00
|
-0.02
|
|
Self-Esteem
|
-0.03
|
0.05
|
-0.05
|
-0.06
|
0.16
|
-0.10
|
1.00
|
-0.12
|
0.28
|
0.16
|
0.00
|
0.06
|
0.04
|
-0.02
|
0.04
|
-0.11
|
0.06
|
-0.17
|
0.16
|
0.07
|
|
Narcissism
|
0.16
|
0.10
|
0.14
|
0.17
|
-0.07
|
0.43
|
-0.12
|
1.00
|
-0.25
|
-0.22
|
0.11
|
0.08
|
-0.02
|
-0.02
|
-0.07
|
0.05
|
0.05
|
0.07
|
-0.14
|
-0.06
|
|
Agreeableness
|
0.03
|
0.09
|
0.03
|
-0.03
|
0.35
|
-0.49
|
0.28
|
-0.25
|
1.00
|
0.20
|
-0.12
|
0.03
|
-0.08
|
-0.04
|
0.12
|
-0.14
|
0.06
|
0.00
|
0.04
|
-0.01
|
|
Openness
|
-0.09
|
-0.03
|
-0.07
|
-0.03
|
0.03
|
-0.11
|
0.16
|
-0.22
|
0.20
|
1.00
|
0.01
|
0.03
|
0.10
|
0.05
|
-0.03
|
0.01
|
0.11
|
-0.10
|
0.15
|
0.12
|
|
People/Bot
|
0.07
|
-0.07
|
0.04
|
0.08
|
0.00
|
0.00
|
0.00
|
0.11
|
-0.12
|
0.01
|
1.00
|
-0.10
|
-0.06
|
-0.03
|
-0.08
|
0.03
|
-0.05
|
0.09
|
-0.16
|
-0.19
|
|
Confidence
|
0.14
|
0.00
|
0.08
|
-0.02
|
0.08
|
0.05
|
0.06
|
0.08
|
0.03
|
0.03
|
-0.10
|
1.00
|
-0.10
|
-0.12
|
-0.05
|
0.02
|
-0.08
|
0.10
|
-0.13
|
-0.15
|
|
Score
|
-0.26
|
-0.15
|
-0.30
|
0.11
|
-0.05
|
-0.01
|
0.04
|
-0.02
|
-0.08
|
0.10
|
-0.06
|
-0.10
|
1.00
|
0.21
|
-0.01
|
0.40
|
0.32
|
-0.69
|
0.56
|
0.47
|
|
Stuns
|
-0.10
|
-0.11
|
-0.12
|
0.11
|
-0.09
|
0.01
|
-0.02
|
-0.02
|
-0.04
|
0.05
|
-0.03
|
-0.12
|
0.21
|
1.00
|
0.17
|
-0.14
|
0.39
|
-0.26
|
0.24
|
0.17
|
|
Raids
|
0.02
|
0.00
|
0.08
|
-0.08
|
-0.03
|
-0.07
|
0.04
|
-0.07
|
0.12
|
-0.03
|
-0.08
|
-0.05
|
-0.01
|
0.17
|
1.00
|
-0.28
|
0.30
|
0.05
|
0.05
|
0.06
|
|
Produces Fruit
|
0.09
|
0.09
|
0.08
|
-0.05
|
-0.04
|
0.11
|
-0.11
|
0.05
|
-0.14
|
0.01
|
0.03
|
0.02
|
0.40
|
-0.14
|
-0.28
|
1.00
|
-0.16
|
-0.11
|
-0.01
|
0.03
|
|
Intent Aggress
|
-0.07
|
-0.10
|
-0.12
|
0.01
|
-0.07
|
0.04
|
0.06
|
0.05
|
0.06
|
0.11
|
-0.05
|
-0.08
|
0.32
|
0.39
|
0.30
|
-0.16
|
1.00
|
-0.29
|
0.36
|
0.31
|
|
Difficulty
|
0.30
|
0.22
|
0.36
|
-0.09
|
0.09
|
-0.02
|
-0.17
|
0.07
|
0.00
|
-0.10
|
0.09
|
0.10
|
-0.69
|
-0.26
|
0.05
|
-0.11
|
-0.29
|
1.00
|
-0.66
|
-0.52
|
|
Positive
|
-0.47
|
-0.24
|
-0.49
|
-0.04
|
0.01
|
0.00
|
0.16
|
-0.14
|
0.04
|
0.15
|
-0.16
|
-0.13
|
0.56
|
0.24
|
0.05
|
-0.01
|
0.36
|
-0.66
|
1.00
|
0.78
|
|
Play Again
|
-0.35
|
-0.10
|
-0.34
|
0.02
|
0.01
|
-0.02
|
0.07
|
-0.06
|
-0.01
|
0.12
|
-0.19
|
-0.15
|
0.47
|
0.17
|
0.06
|
0.03
|
0.31
|
-0.52
|
0.78
|
1.00
|
7.2 Correlations — Easy Mode Only
cor_easy <- df %>%
filter(cond == "easy") %>%
dplyr::select(names(df)[names(df) %in% c(
"moral_outrage", "surprised", "disappointed", "felt_guilt",
"gp", "sadism", "selfesteem", "narcissism",
"tipi_agreeableness", "tipi_openness",
"people_or_bot", "confidence",
"Player_score", "total_stuns", "Player_raids_hut",
"Player_produces_fruit", "intent_aggression",
"difficulty", "positive", "play_again"
)]) %>%
mutate(across(everything(), as.numeric))
names(cor_easy) <- names(cor_vars)
cor(cor_easy, use = "pairwise.complete.obs") %>%
round(2) %>%
kable(caption = "Pairwise correlations — Easy mode only") %>%
kable_styling(bootstrap_options = c("striped", "condensed"),
font_size = 10, full_width = TRUE) %>%
scroll_box(width = "100%")
Pairwise correlations — Easy mode only
|
|
Moral Outrage
|
Surprised
|
Disappointed
|
Felt Guilt
|
Guilt Proneness
|
Sadism
|
Self-Esteem
|
Narcissism
|
Agreeableness
|
Openness
|
People/Bot
|
Confidence
|
Score
|
Stuns
|
Raids
|
Produces Fruit
|
Intent Aggress
|
Difficulty
|
Positive
|
Play Again
|
|
Moral Outrage
|
1.00
|
0.19
|
0.27
|
0.06
|
-0.15
|
-0.06
|
-0.55
|
-0.42
|
0.08
|
-0.04
|
-0.54
|
-0.21
|
-0.25
|
0.01
|
-0.06
|
0.14
|
-0.10
|
0.22
|
-0.33
|
-0.27
|
|
Surprised
|
0.19
|
1.00
|
0.69
|
0.22
|
-0.04
|
0.09
|
-0.29
|
-0.16
|
-0.07
|
-0.02
|
-0.10
|
-0.01
|
-0.05
|
0.11
|
-0.10
|
0.16
|
-0.03
|
0.60
|
-0.11
|
-0.12
|
|
Disappointed
|
0.27
|
0.69
|
1.00
|
0.22
|
-0.07
|
0.01
|
-0.48
|
-0.31
|
-0.02
|
0.03
|
-0.17
|
0.08
|
-0.08
|
0.04
|
-0.10
|
0.08
|
-0.09
|
0.84
|
-0.15
|
-0.10
|
|
Felt Guilt
|
0.06
|
0.22
|
0.22
|
1.00
|
-0.07
|
0.36
|
-0.20
|
-0.14
|
0.09
|
0.12
|
0.01
|
0.01
|
-0.07
|
-0.02
|
-0.10
|
0.07
|
0.06
|
0.20
|
-0.05
|
0.14
|
|
Guilt Proneness
|
-0.15
|
-0.04
|
-0.07
|
-0.07
|
1.00
|
-0.08
|
0.09
|
0.09
|
-0.07
|
0.12
|
-0.06
|
-0.01
|
-0.08
|
0.31
|
0.15
|
0.22
|
-0.10
|
-0.05
|
-0.04
|
-0.13
|
|
Sadism
|
-0.06
|
0.09
|
0.01
|
0.36
|
-0.08
|
1.00
|
0.01
|
0.04
|
0.16
|
-0.09
|
0.19
|
0.01
|
0.07
|
-0.27
|
-0.22
|
-0.03
|
0.37
|
-0.03
|
-0.01
|
0.19
|
|
Self-Esteem
|
-0.55
|
-0.29
|
-0.48
|
-0.20
|
0.09
|
0.01
|
1.00
|
0.80
|
-0.15
|
0.04
|
0.41
|
0.23
|
0.14
|
0.08
|
0.07
|
0.01
|
0.11
|
-0.46
|
0.46
|
0.23
|
|
Narcissism
|
-0.42
|
-0.16
|
-0.31
|
-0.14
|
0.09
|
0.04
|
0.80
|
1.00
|
-0.15
|
-0.04
|
0.37
|
0.23
|
0.14
|
0.04
|
0.04
|
-0.01
|
-0.02
|
-0.34
|
0.37
|
0.19
|
|
Agreeableness
|
0.08
|
-0.07
|
-0.02
|
0.09
|
-0.07
|
0.16
|
-0.15
|
-0.15
|
1.00
|
-0.23
|
-0.03
|
-0.04
|
0.03
|
-0.19
|
-0.08
|
0.00
|
-0.11
|
0.03
|
-0.03
|
-0.03
|
|
Openness
|
-0.04
|
-0.02
|
0.03
|
0.12
|
0.12
|
-0.09
|
0.04
|
-0.04
|
-0.23
|
1.00
|
0.02
|
0.07
|
-0.03
|
0.11
|
0.11
|
0.17
|
0.03
|
0.03
|
0.01
|
0.00
|
|
People/Bot
|
-0.54
|
-0.10
|
-0.17
|
0.01
|
-0.06
|
0.19
|
0.41
|
0.37
|
-0.03
|
0.02
|
1.00
|
0.31
|
0.69
|
-0.13
|
0.06
|
-0.10
|
0.13
|
-0.19
|
0.40
|
0.17
|
|
Confidence
|
-0.21
|
-0.01
|
0.08
|
0.01
|
-0.01
|
0.01
|
0.23
|
0.23
|
-0.04
|
0.07
|
0.31
|
1.00
|
-0.09
|
0.15
|
-0.12
|
-0.03
|
0.01
|
0.08
|
0.39
|
0.19
|
|
Score
|
-0.25
|
-0.05
|
-0.08
|
-0.07
|
-0.08
|
0.07
|
0.14
|
0.14
|
0.03
|
-0.03
|
0.69
|
-0.09
|
1.00
|
-0.14
|
0.10
|
-0.10
|
0.12
|
-0.11
|
0.04
|
-0.09
|
|
Stuns
|
0.01
|
0.11
|
0.04
|
-0.02
|
0.31
|
-0.27
|
0.08
|
0.04
|
-0.19
|
0.11
|
-0.13
|
0.15
|
-0.14
|
1.00
|
0.30
|
0.35
|
-0.44
|
0.06
|
0.04
|
-0.08
|
|
Raids
|
-0.06
|
-0.10
|
-0.10
|
-0.10
|
0.15
|
-0.22
|
0.07
|
0.04
|
-0.08
|
0.11
|
0.06
|
-0.12
|
0.10
|
0.30
|
1.00
|
0.02
|
-0.16
|
-0.10
|
0.07
|
-0.06
|
|
Produces Fruit
|
0.14
|
0.16
|
0.08
|
0.07
|
0.22
|
-0.03
|
0.01
|
-0.01
|
0.00
|
0.17
|
-0.10
|
-0.03
|
-0.10
|
0.35
|
0.02
|
1.00
|
-0.29
|
0.11
|
-0.05
|
-0.12
|
|
Intent Aggress
|
-0.10
|
-0.03
|
-0.09
|
0.06
|
-0.10
|
0.37
|
0.11
|
-0.02
|
-0.11
|
0.03
|
0.13
|
0.01
|
0.12
|
-0.44
|
-0.16
|
-0.29
|
1.00
|
-0.09
|
0.00
|
0.04
|
|
Difficulty
|
0.22
|
0.60
|
0.84
|
0.20
|
-0.05
|
-0.03
|
-0.46
|
-0.34
|
0.03
|
0.03
|
-0.19
|
0.08
|
-0.11
|
0.06
|
-0.10
|
0.11
|
-0.09
|
1.00
|
-0.08
|
-0.05
|
|
Positive
|
-0.33
|
-0.11
|
-0.15
|
-0.05
|
-0.04
|
-0.01
|
0.46
|
0.37
|
-0.03
|
0.01
|
0.40
|
0.39
|
0.04
|
0.04
|
0.07
|
-0.05
|
0.00
|
-0.08
|
1.00
|
0.40
|
|
Play Again
|
-0.27
|
-0.12
|
-0.10
|
0.14
|
-0.13
|
0.19
|
0.23
|
0.19
|
-0.03
|
0.00
|
0.17
|
0.19
|
-0.09
|
-0.08
|
-0.06
|
-0.12
|
0.04
|
-0.05
|
0.40
|
1.00
|
7.3 Correlations — Hard Mode Only
cor_hard <- df %>%
filter(cond == "hard") %>%
dplyr::select(names(df)[names(df) %in% c(
"moral_outrage", "surprised", "disappointed", "felt_guilt",
"gp", "sadism", "selfesteem", "narcissism",
"tipi_agreeableness", "tipi_openness",
"people_or_bot", "confidence",
"Player_score", "total_stuns", "Player_raids_hut",
"Player_produces_fruit", "intent_aggression",
"difficulty", "positive", "play_again"
)]) %>%
mutate(across(everything(), as.numeric))
names(cor_hard) <- names(cor_vars)
cor(cor_hard, use = "pairwise.complete.obs") %>%
round(2) %>%
kable(caption = "Pairwise correlations — Hard mode only") %>%
kable_styling(bootstrap_options = c("striped", "condensed"),
font_size = 10, full_width = TRUE) %>%
scroll_box(width = "100%")
Pairwise correlations — Hard mode only
|
|
Moral Outrage
|
Surprised
|
Disappointed
|
Felt Guilt
|
Guilt Proneness
|
Sadism
|
Self-Esteem
|
Narcissism
|
Agreeableness
|
Openness
|
People/Bot
|
Confidence
|
Score
|
Stuns
|
Raids
|
Produces Fruit
|
Intent Aggress
|
Difficulty
|
Positive
|
Play Again
|
|
Moral Outrage
|
1.00
|
0.15
|
0.15
|
-0.12
|
-0.16
|
-0.02
|
-0.46
|
-0.35
|
0.05
|
0.08
|
0.03
|
0.03
|
0.21
|
-0.02
|
0.00
|
0.10
|
-0.15
|
0.21
|
-0.08
|
-0.09
|
|
Surprised
|
0.15
|
1.00
|
0.56
|
-0.02
|
0.17
|
0.08
|
-0.08
|
0.09
|
-0.09
|
-0.02
|
-0.04
|
-0.05
|
0.23
|
0.07
|
0.06
|
0.14
|
0.02
|
0.49
|
-0.05
|
-0.06
|
|
Disappointed
|
0.15
|
0.56
|
1.00
|
0.01
|
0.03
|
0.16
|
-0.32
|
-0.19
|
0.08
|
0.04
|
-0.11
|
-0.04
|
0.28
|
0.03
|
0.01
|
0.08
|
0.06
|
0.73
|
0.01
|
-0.05
|
|
Felt Guilt
|
-0.12
|
-0.02
|
0.01
|
1.00
|
-0.10
|
0.10
|
-0.04
|
0.12
|
0.10
|
-0.14
|
-0.18
|
-0.09
|
-0.05
|
-0.05
|
0.01
|
0.17
|
0.19
|
0.05
|
0.03
|
0.00
|
|
Guilt Proneness
|
-0.16
|
0.17
|
0.03
|
-0.10
|
1.00
|
-0.13
|
0.20
|
-0.05
|
0.09
|
0.04
|
0.06
|
0.12
|
-0.15
|
0.24
|
0.16
|
0.07
|
-0.09
|
0.03
|
0.13
|
0.12
|
|
Sadism
|
-0.02
|
0.08
|
0.16
|
0.10
|
-0.13
|
1.00
|
-0.15
|
-0.03
|
0.07
|
0.15
|
0.05
|
-0.15
|
0.06
|
-0.26
|
-0.21
|
-0.10
|
0.46
|
0.23
|
0.15
|
-0.18
|
|
Self-Esteem
|
-0.46
|
-0.08
|
-0.32
|
-0.04
|
0.20
|
-0.15
|
1.00
|
0.61
|
-0.19
|
-0.18
|
0.29
|
0.18
|
-0.28
|
0.01
|
0.15
|
0.01
|
0.01
|
-0.39
|
0.14
|
0.16
|
|
Narcissism
|
-0.35
|
0.09
|
-0.19
|
0.12
|
-0.05
|
-0.03
|
0.61
|
1.00
|
-0.23
|
-0.17
|
0.24
|
0.11
|
-0.16
|
-0.09
|
0.13
|
0.05
|
0.08
|
-0.24
|
0.15
|
0.00
|
|
Agreeableness
|
0.05
|
-0.09
|
0.08
|
0.10
|
0.09
|
0.07
|
-0.19
|
-0.23
|
1.00
|
0.04
|
-0.13
|
-0.15
|
0.04
|
-0.04
|
0.11
|
0.02
|
0.11
|
0.09
|
-0.06
|
-0.03
|
|
Openness
|
0.08
|
-0.02
|
0.04
|
-0.14
|
0.04
|
0.15
|
-0.18
|
-0.17
|
0.04
|
1.00
|
0.06
|
-0.23
|
0.10
|
-0.05
|
-0.01
|
-0.04
|
0.04
|
0.19
|
-0.13
|
-0.26
|
|
People/Bot
|
0.03
|
-0.04
|
-0.11
|
-0.18
|
0.06
|
0.05
|
0.29
|
0.24
|
-0.13
|
0.06
|
1.00
|
-0.01
|
0.03
|
-0.16
|
-0.02
|
0.02
|
0.02
|
-0.14
|
0.06
|
0.22
|
|
Confidence
|
0.03
|
-0.05
|
-0.04
|
-0.09
|
0.12
|
-0.15
|
0.18
|
0.11
|
-0.15
|
-0.23
|
-0.01
|
1.00
|
-0.40
|
0.10
|
0.07
|
-0.03
|
-0.16
|
-0.11
|
0.32
|
0.25
|
|
Score
|
0.21
|
0.23
|
0.28
|
-0.05
|
-0.15
|
0.06
|
-0.28
|
-0.16
|
0.04
|
0.10
|
0.03
|
-0.40
|
1.00
|
-0.14
|
-0.09
|
0.02
|
0.12
|
0.32
|
-0.39
|
-0.24
|
|
Stuns
|
-0.02
|
0.07
|
0.03
|
-0.05
|
0.24
|
-0.26
|
0.01
|
-0.09
|
-0.04
|
-0.05
|
-0.16
|
0.10
|
-0.14
|
1.00
|
0.11
|
0.35
|
-0.55
|
0.00
|
0.08
|
0.02
|
|
Raids
|
0.00
|
0.06
|
0.01
|
0.01
|
0.16
|
-0.21
|
0.15
|
0.13
|
0.11
|
-0.01
|
-0.02
|
0.07
|
-0.09
|
0.11
|
1.00
|
0.03
|
-0.06
|
-0.04
|
0.12
|
0.18
|
|
Produces Fruit
|
0.10
|
0.14
|
0.08
|
0.17
|
0.07
|
-0.10
|
0.01
|
0.05
|
0.02
|
-0.04
|
0.02
|
-0.03
|
0.02
|
0.35
|
0.03
|
1.00
|
-0.36
|
0.02
|
-0.09
|
-0.02
|
|
Intent Aggress
|
-0.15
|
0.02
|
0.06
|
0.19
|
-0.09
|
0.46
|
0.01
|
0.08
|
0.11
|
0.04
|
0.02
|
-0.16
|
0.12
|
-0.55
|
-0.06
|
-0.36
|
1.00
|
0.10
|
0.11
|
0.00
|
|
Difficulty
|
0.21
|
0.49
|
0.73
|
0.05
|
0.03
|
0.23
|
-0.39
|
-0.24
|
0.09
|
0.19
|
-0.14
|
-0.11
|
0.32
|
0.00
|
-0.04
|
0.02
|
0.10
|
1.00
|
0.01
|
-0.11
|
|
Positive
|
-0.08
|
-0.05
|
0.01
|
0.03
|
0.13
|
0.15
|
0.14
|
0.15
|
-0.06
|
-0.13
|
0.06
|
0.32
|
-0.39
|
0.08
|
0.12
|
-0.09
|
0.11
|
0.01
|
1.00
|
0.35
|
|
Play Again
|
-0.09
|
-0.06
|
-0.05
|
0.00
|
0.12
|
-0.18
|
0.16
|
0.00
|
-0.03
|
-0.26
|
0.22
|
0.25
|
-0.24
|
0.02
|
0.18
|
-0.02
|
0.00
|
-0.11
|
0.35
|
1.00
|
8. Main Analysis: Condition & Emotional Responses
8.1 Does Condition Predict Moral Outrage?
m_out <- lm(moral_outrage ~ cond, data = df)
summary(m_out)
##
## Call:
## lm(formula = moral_outrage ~ cond, data = df)
##
## Residuals:
## Min 1Q Median 3Q Max
## -2.4558 -1.5736 -0.5736 1.4264 4.4264
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 2.5736 0.1672 15.395 < 2e-16 ***
## condhard 0.8822 0.2424 3.639 0.000334 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1.899 on 244 degrees of freedom
## Multiple R-squared: 0.05149, Adjusted R-squared: 0.0476
## F-statistic: 13.24 on 1 and 244 DF, p-value: 0.0003337
People were more outraged in hard mode.
8.2 Does Condition Predict Felt Guilt?
m_guilt_cond <- lm(as.numeric(felt_guilt) ~ cond, data = df)
summary(m_guilt_cond)
##
## Call:
## lm(formula = as.numeric(felt_guilt) ~ cond, data = df)
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.8760 -0.8760 -0.4615 0.1240 5.5385
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 1.8760 0.1023 18.347 <2e-16 ***
## condhard -0.4144 0.1483 -2.795 0.0056 **
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1.161 on 244 degrees of freedom
## Multiple R-squared: 0.03103, Adjusted R-squared: 0.02706
## F-statistic: 7.813 on 1 and 244 DF, p-value: 0.0056
df %>%
group_by(cond) %>%
summarise(M = round(mean(as.numeric(felt_guilt), na.rm = TRUE), 2),
SD = round(sd(as.numeric(felt_guilt), na.rm = TRUE), 2)) %>%
kable(col.names = c("Condition", "Felt Guilt M", "SD"),
caption = "Felt guilt by condition") %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Felt guilt by condition
|
Condition
|
Felt Guilt M
|
SD
|
|
easy
|
1.88
|
1.31
|
|
hard
|
1.46
|
0.97
|
People felt less guilt in hard mode.
8.3 Emotion Profile by Condition
df %>%
mutate(across(c(moral_outrage, surprised, disappointed,
felt_guilt, positive), as.numeric)) %>%
dplyr::select(cond, moral_outrage, surprised, disappointed, felt_guilt, positive) %>%
pivot_longer(-cond, names_to = "Emotion", values_to = "Score") %>%
mutate(Emotion = recode(Emotion,
"moral_outrage" = "Moral Outrage",
"surprised" = "Surprised",
"disappointed" = "Disappointed",
"felt_guilt" = "Felt Guilt",
"positive" = "Positive")) %>%
group_by(cond, Emotion) %>%
summarise(M = mean(Score, na.rm = TRUE),
SE = sd(Score, na.rm = TRUE) / sqrt(n()), .groups = "drop") %>%
ggplot(aes(x = Emotion, y = M, fill = cond)) +
geom_col(position = position_dodge(0.6), width = 0.5) +
geom_errorbar(aes(ymin = M - SE, ymax = M + SE),
position = position_dodge(0.6), width = 0.2) +
scale_fill_manual(values = c("easy" = "#5B8DB8", "hard" = "#E07B54"),
name = "Condition") +
scale_y_continuous(limits = c(0, 7)) +
labs(title = "Emotional Responses by Condition",
subtitle = "Error bars = ±1 SE",
x = NULL, y = "Mean (1–7)") +
theme_minimal()

9. Guilt Proneness as Moderator
9.1 GP Distribution
ggplot(df, aes(x = gp)) +
geom_histogram(bins = 20, fill = "#6BAE75", color = "white") +
geom_vline(xintercept = mean(df$gp, na.rm = TRUE),
linetype = "dashed", color = "#E07B54", linewidth = 0.8) +
annotate("text",
x = mean(df$gp, na.rm = TRUE) + 0.1,
y = Inf, vjust = 1.5,
label = paste0("M = ", round(mean(df$gp, na.rm = TRUE), 2)),
color = "#E07B54", size = 3.5) +
labs(title = "Distribution of GP-5", x = "Guilt Proneness (1–5)", y = "Count") +
theme_minimal()

# Balance check
t.test(gp ~ cond, data = df)
##
## Welch Two Sample t-test
##
## data: gp by cond
## t = 0.1426, df = 242.49, p-value = 0.8867
## alternative hypothesis: true difference in means between group easy and group hard is not equal to 0
## 95 percent confidence interval:
## -0.1772573 0.2049257
## sample estimates:
## mean in group easy mean in group hard
## 3.998450 3.984615
9.2 GP × Condition → Behavioral Aggression (Total)
m_gp_agg <- lm(total_aggression ~ cond * gp_c, data = df)
summary(m_gp_agg)
##
## Call:
## lm(formula = total_aggression ~ cond * gp_c, data = df)
##
## Residuals:
## Min 1Q Median 3Q Max
## -10.981 -5.261 -0.776 3.796 38.906
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 10.3251 0.6246 16.531 <2e-16 ***
## condhard -0.4896 0.9057 -0.541 0.589
## gp_c -0.8478 0.5796 -1.463 0.145
## condhard:gp_c 0.6295 0.9298 0.677 0.499
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 7.094 on 242 degrees of freedom
## Multiple R-squared: 0.01027, Adjusted R-squared: -0.002001
## F-statistic: 0.8369 on 3 and 242 DF, p-value: 0.4747
interact_plot(m_gp_agg,
pred = cond,
modx = gp_c,
modx.values = c(-1, 0, 1),
modx.labels = c("Low GP (−1 SD)", "Mean GP", "High GP (+1 SD)"),
x.label = "Condition",
y.label = "Total Aggression (stuns + raids)",
main.title = "GP × Condition on Total Aggression",
legend.main = "Guilt Proneness") +
theme_minimal()

9.3 GP × Condition → Stuns (Aggressive Acts Only)
m_gp_stuns <- lm(total_stuns ~ cond * gp_c, data = df)
summary(m_gp_stuns)
##
## Call:
## lm(formula = total_stuns ~ cond * gp_c, data = df)
##
## Residuals:
## Min 1Q Median 3Q Max
## -8.481 -4.227 -0.841 3.587 39.348
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 7.8673 0.5266 14.941 <2e-16 ***
## condhard -1.6973 0.7635 -2.223 0.0271 *
## gp_c -0.7927 0.4886 -1.622 0.1060
## condhard:gp_c 0.6871 0.7839 0.877 0.3816
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 5.98 on 242 degrees of freedom
## Multiple R-squared: 0.03029, Adjusted R-squared: 0.01827
## F-statistic: 2.52 on 3 and 242 DF, p-value: 0.05864
interact_plot(m_gp_stuns,
pred = cond,
modx = gp_c,
modx.values = c(-1, 0, 1),
modx.labels = c("Low GP (−1 SD)", "Mean GP", "High GP (+1 SD)"),
x.label = "Condition",
y.label = "Total Stuns",
main.title = "GP × Condition on Stuns",
legend.main = "Guilt Proneness") +
theme_minimal()

9.4 GP × Condition → Raids (Stealing)
m_gp_raids <- lm(as.numeric(Player_raids_hut) ~ cond * gp_c, data = df)
summary(m_gp_raids)
##
## Call:
## lm(formula = as.numeric(Player_raids_hut) ~ cond * gp_c, data = df)
##
## Residuals:
## Min 1Q Median 3Q Max
## -3.8705 -2.3961 -0.4501 1.5427 11.3945
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 2.45784 0.24517 10.025 < 2e-16 ***
## condhard 1.20776 0.35551 3.397 0.000796 ***
## gp_c -0.05506 0.22751 -0.242 0.808980
## condhard:gp_c -0.05756 0.36499 -0.158 0.874816
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 2.785 on 242 degrees of freedom
## Multiple R-squared: 0.04644, Adjusted R-squared: 0.03462
## F-statistic: 3.929 on 3 and 242 DF, p-value: 0.009171
interact_plot(m_gp_raids,
pred = cond,
modx = gp_c,
modx.values = c(-1, 0, 1),
modx.labels = c("Low GP (−1 SD)", "Mean GP", "High GP (+1 SD)"),
x.label = "Condition",
y.label = "Raids (hut steals)",
main.title = "GP × Condition on Raids",
legend.main = "Guilt Proneness") +
theme_minimal()

9.5 GP × Condition → Subjective Intent to Aggress
m_gp_intent <- lm(intent_aggression ~ cond * gp_c, data = df)
summary(m_gp_intent)
##
## Call:
## lm(formula = intent_aggression ~ cond * gp_c, data = df)
##
## Residuals:
## Min 1Q Median 3Q Max
## -3.4098 -1.1077 -0.1247 1.1469 3.2948
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 4.34951 0.13762 31.605 < 2e-16 ***
## condhard -0.52191 0.19956 -2.615 0.00947 **
## gp_c -0.07794 0.12771 -0.610 0.54223
## condhard:gp_c -0.07599 0.20488 -0.371 0.71103
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1.563 on 242 degrees of freedom
## Multiple R-squared: 0.03231, Adjusted R-squared: 0.02032
## F-statistic: 2.694 on 3 and 242 DF, p-value: 0.04674
interact_plot(m_gp_intent,
pred = cond,
modx = gp_c,
modx.values = c(-1, 0, 1),
modx.labels = c("Low GP (−1 SD)", "Mean GP", "High GP (+1 SD)"),
x.label = "Condition",
y.label = "Intent to Aggress (self-report)",
main.title = "GP × Condition on Intent to Aggress",
legend.main = "Guilt Proneness") +
theme_minimal()

Damn it!!!
9.6 GP × Condition → Felt Guilt (Post-game)
m_gp_fglt <- lm(as.numeric(felt_guilt) ~ cond * gp_c, data = df)
summary(m_gp_fglt)
##
## Call:
## lm(formula = as.numeric(felt_guilt) ~ cond * gp_c, data = df)
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.9945 -0.7814 -0.4170 0.2541 5.3901
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 1.87519 0.10198 18.388 < 2e-16 ***
## condhard -0.41190 0.14787 -2.786 0.00577 **
## gp_c 0.09055 0.09463 0.957 0.33958
## condhard:gp_c 0.09391 0.15181 0.619 0.53677
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1.158 on 242 degrees of freedom
## Multiple R-squared: 0.04418, Adjusted R-squared: 0.03233
## F-statistic: 3.729 on 3 and 242 DF, p-value: 0.01196
interact_plot(m_gp_fglt,
pred = cond,
modx = gp_c,
modx.values = c(-1, 0, 1),
modx.labels = c("Low GP (−1 SD)", "Mean GP", "High GP (+1 SD)"),
x.label = "Condition",
y.label = "Felt Guilt (post-game)",
main.title = "GP × Condition on Felt Guilt",
legend.main = "Guilt Proneness") +
theme_minimal()

9.7 GP × Condition → Moral Outrage
m_gp_out <- lm(moral_outrage ~ cond * gp_c, data = df)
summary(m_gp_out)
##
## Call:
## lm(formula = moral_outrage ~ cond * gp_c, data = df)
##
## Residuals:
## Min 1Q Median 3Q Max
## -2.5170 -1.5022 -0.4728 1.3810 5.0840
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 2.5720 0.1673 15.372 < 2e-16 ***
## condhard 0.8843 0.2426 3.645 0.000327 ***
## gp_c 0.1936 0.1553 1.247 0.213648
## condhard:gp_c -0.1475 0.2491 -0.592 0.554203
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1.9 on 242 degrees of freedom
## Multiple R-squared: 0.05776, Adjusted R-squared: 0.04608
## F-statistic: 4.945 on 3 and 242 DF, p-value: 0.002378
interact_plot(m_gp_out,
pred = cond,
modx = gp_c,
modx.values = c(-1, 0, 1),
modx.labels = c("Low GP (−1 SD)", "Mean GP", "High GP (+1 SD)"),
x.label = "Condition",
y.label = "Moral Outrage",
main.title = "GP × Condition on Moral Outrage",
legend.main = "Guilt Proneness") +
theme_minimal()

10. People or Bot: Belief About Opponents
10.1 Overall Breakdown
df %>%
filter(!is.na(people_or_bot_label)) %>%
count(people_or_bot_label) %>%
mutate(pct = paste0(round(n / sum(n) * 100, 1), "%")) %>%
kable(col.names = c("Response", "N", "%"),
caption = "Did participants think they were playing real people?") %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Did participants think they were playing real people?
|
Response
|
N
|
%
|
|
Real people
|
53
|
21.5%
|
|
Unsure
|
35
|
14.2%
|
|
Computer/bots
|
158
|
64.2%
|
df %>%
filter(!is.na(people_or_bot_label)) %>%
count(people_or_bot_label) %>%
mutate(pct = n / sum(n)) %>%
ggplot(aes(x = people_or_bot_label, y = pct, fill = people_or_bot_label)) +
geom_col(show.legend = FALSE) +
geom_text(aes(label = paste0(n, "\n(", percent(pct, 1), ")")),
vjust = -0.3, size = 3.5) +
scale_y_continuous(labels = percent_format(), limits = c(0, 0.8)) +
scale_fill_manual(values = c("Real people" = "#6BAE75",
"Unsure" = "#F0C274",
"Computer/bots" = "#5B8DB8")) +
labs(title = "Belief About Game Opponents", x = NULL, y = "Proportion") +
theme_minimal()

10.2 By Condition
df %>%
filter(!is.na(people_or_bot_label)) %>%
count(cond, people_or_bot_label) %>%
group_by(cond) %>%
mutate(pct = paste0(round(n / sum(n) * 100, 1), "%")) %>%
kable(col.names = c("Condition", "Response", "N", "%"),
caption = "Belief about opponents by condition") %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Belief about opponents by condition
|
Condition
|
Response
|
N
|
%
|
|
easy
|
Real people
|
32
|
24.8%
|
|
easy
|
Unsure
|
19
|
14.7%
|
|
easy
|
Computer/bots
|
78
|
60.5%
|
|
hard
|
Real people
|
21
|
17.9%
|
|
hard
|
Unsure
|
16
|
13.7%
|
|
hard
|
Computer/bots
|
80
|
68.4%
|
# Chi-square test: is belief independent of condition?
chisq.test(table(df$cond, df$people_or_bot_label))
##
## Pearson's Chi-squared test
##
## data: table(df$cond, df$people_or_bot_label)
## X-squared = 1.9848, df = 2, p-value = 0.3707
10.3 Belief → Felt Guilt
Hypothesis: believing you’re playing real people should increase
felt guilt.
m_pob_guilt <- lm(as.numeric(felt_guilt) ~ humanness + cond, data = df)
summary(m_pob_guilt)
##
## Call:
## lm(formula = as.numeric(felt_guilt) ~ humanness + cond, data = df)
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.9520 -0.7157 -0.5201 0.2843 5.5980
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 1.95198 0.11748 16.615 < 2e-16 ***
## humanness -0.11814 0.09032 -1.308 0.19212
## condhard -0.43188 0.14865 -2.905 0.00401 **
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1.16 on 243 degrees of freedom
## Multiple R-squared: 0.0378, Adjusted R-squared: 0.02988
## F-statistic: 4.773 on 2 and 243 DF, p-value: 0.009262
# Visualize: felt guilt by belief category
df %>%
filter(!is.na(people_or_bot_label)) %>%
group_by(people_or_bot_label, cond) %>%
summarise(M = mean(as.numeric(felt_guilt), na.rm = TRUE),
SE = sd(as.numeric(felt_guilt), na.rm = TRUE) / sqrt(n()),
.groups = "drop") %>%
ggplot(aes(x = people_or_bot_label, y = M, fill = cond)) +
geom_col(position = position_dodge(0.6), width = 0.5) +
geom_errorbar(aes(ymin = M - SE, ymax = M + SE),
position = position_dodge(0.6), width = 0.2) +
scale_fill_manual(values = c("easy" = "#5B8DB8", "hard" = "#E07B54"),
name = "Condition") +
scale_y_continuous(limits = c(0, 7)) +
labs(title = "Felt Guilt by Opponent Belief and Condition",
x = "Believed opponents were...", y = "Felt Guilt (1–7)") +
theme_minimal()

10.4 Belief → Aggression
Do people who think they’re playing real people aggress
less?
m_pob_agg_intent <- lm(intent_aggression ~ humanness + cond, data = df)
m_pob_agg_behav <- lm(total_aggression ~ humanness + cond, data = df)
summary(m_pob_agg_intent)
##
## Call:
## lm(formula = intent_aggression ~ humanness + cond, data = df)
##
## Residuals:
## Min 1Q Median 3Q Max
## -3.3823 -0.9761 -0.1293 1.2174 3.2174
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 4.28849 0.15825 27.100 <2e-16 ***
## humanness 0.09379 0.12166 0.771 0.4415
## condhard -0.50593 0.20023 -2.527 0.0122 *
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1.562 on 243 degrees of freedom
## Multiple R-squared: 0.0295, Adjusted R-squared: 0.02152
## F-statistic: 3.694 on 2 and 243 DF, p-value: 0.02629
summary(m_pob_agg_behav)
##
## Call:
## lm(formula = total_aggression ~ humanness + cond, data = df)
##
## Residuals:
## Min 1Q Median 3Q Max
## -11.462 -4.775 -0.775 3.698 39.225
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 9.7751 0.7170 13.633 <2e-16 ***
## humanness 0.8435 0.5513 1.530 0.127
## condhard -0.3557 0.9072 -0.392 0.695
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 7.078 on 243 degrees of freedom
## Multiple R-squared: 0.01068, Adjusted R-squared: 0.002539
## F-statistic: 1.312 on 2 and 243 DF, p-value: 0.2712
# Visualize behavioral aggression by belief
df %>%
filter(!is.na(people_or_bot_label)) %>%
group_by(people_or_bot_label) %>%
summarise(M = mean(total_aggression, na.rm = TRUE),
SE = sd(total_aggression, na.rm = TRUE) / sqrt(n()),
.groups = "drop") %>%
ggplot(aes(x = people_or_bot_label, y = M, fill = people_or_bot_label)) +
geom_col(width = 0.5, show.legend = FALSE) +
geom_errorbar(aes(ymin = M - SE, ymax = M + SE), width = 0.15) +
scale_fill_manual(values = c("Real people" = "#6BAE75",
"Unsure" = "#F0C274",
"Computer/bots" = "#5B8DB8")) +
labs(title = "Total Aggression by Belief About Opponents",
x = "Believed opponents were...", y = "Total Aggression") +
theme_minimal()

10.6 GP Moderates Belief → Felt Guilt
High GP individuals may be especially sensitive to believing they
hurt real people.
m_pob_gp <- lm(as.numeric(felt_guilt) ~ humanness * gp_c + cond, data = df)
summary(m_pob_gp)
##
## Call:
## lm(formula = as.numeric(felt_guilt) ~ humanness * gp_c + cond,
## data = df)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1.2120 -0.7015 -0.3660 0.2930 5.5344
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 1.93504 0.11705 16.531 < 2e-16 ***
## humanness -0.11534 0.08978 -1.285 0.20010
## gp_c 0.21012 0.09250 2.272 0.02400 *
## condhard -0.40648 0.14842 -2.739 0.00663 **
## humanness:gp_c -0.14420 0.09415 -1.532 0.12694
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1.152 on 241 degrees of freedom
## Multiple R-squared: 0.05812, Adjusted R-squared: 0.04249
## F-statistic: 3.718 on 4 and 241 DF, p-value: 0.005886
interact_plot(m_pob_gp,
pred = humanness,
modx = gp_c,
modx.values = c(-1, 0, 1),
modx.labels = c("Low GP (−1 SD)", "Mean GP", "High GP (+1 SD)"),
x.label = "Humanness (0=Bot, 1=Unsure, 2=Real)",
y.label = "Felt Guilt",
main.title = "GP Moderates Belief → Felt Guilt",
legend.main = "Guilt Proneness") +
theme_minimal()

10.7 Including confience in their judgement
# Confidence in people-or-bot judgment
df %>%
filter(!is.na(people_or_bot_label)) %>%
group_by(people_or_bot_label) %>%
summarise(
N = n(),
Confidence_M = round(mean(as.numeric(confidence), na.rm = TRUE), 2),
Confidence_SD = round(sd(as.numeric(confidence), na.rm = TRUE), 2)
) %>%
kable(col.names = c("Belief", "N", "Confidence M", "Confidence SD"),
caption = "Confidence in opponent belief by response category (1–7)") %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Confidence in opponent belief by response category (1–7)
|
Belief
|
N
|
Confidence M
|
Confidence SD
|
|
Real people
|
53
|
4.40
|
1.68
|
|
Unsure
|
35
|
3.37
|
1.94
|
|
Computer/bots
|
158
|
5.50
|
1.46
|
# Visualize confidence by belief category
df %>%
filter(!is.na(people_or_bot_label)) %>%
group_by(people_or_bot_label) %>%
summarise(M = mean(as.numeric(confidence), na.rm = TRUE),
SE = sd(as.numeric(confidence), na.rm = TRUE) / sqrt(n())) %>%
ggplot(aes(x = people_or_bot_label, y = M, fill = people_or_bot_label)) +
geom_col(width = 0.5, show.legend = FALSE) +
geom_errorbar(aes(ymin = M - SE, ymax = M + SE), width = 0.15) +
scale_fill_manual(values = c("Real people" = "#6BAE75",
"Unsure" = "#F0C274",
"Computer/bots" = "#5B8DB8")) +
scale_y_continuous(limits = c(0, 7)) +
labs(title = "Confidence in Opponent Belief by Category",
subtitle = "Higher = more certain in their judgment",
x = NULL, y = "Mean Confidence (1–7)") +
theme_minimal()

# Create combined belief x confidence variable
# Ranges from -7 (very confident bots) to +7 (very confident real people)
# Unsure anchored at 0 regardless of confidence
df <- df %>%
mutate(
confidence_n = as.numeric(confidence),
conf_c = as.numeric(scale(confidence_n)),
humanness_certain = case_when(
people_or_bot == 1 ~ confidence_n,
people_or_bot == 2 ~ -confidence_n,
people_or_bot == 3 ~ 0,
TRUE ~ NA_real_
),
humanness_certain_c = as.numeric(scale(humanness_certain))
)
# Distribution of the combined variable
ggplot(df, aes(x = humanness_certain)) +
geom_histogram(bins = 25, fill = "#6BAE75", color = "white") +
geom_vline(xintercept = 0, linetype = "dashed", color = "#E07B54") +
annotate("text", x = -5, y = Inf, vjust = 1.5,
label = "Confident\nit's bots", color = "#E07B54", size = 3.5) +
annotate("text", x = 5, y = Inf, vjust = 1.5,
label = "Confident\nreal people", color = "#6BAE75", size = 3.5) +
labs(title = "Belief x Confidence Combined Variable",
subtitle = "-7 = very confident bots, 0 = unsure, +7 = very confident real people",
x = "Humanness (confidence-weighted)", y = "Count") +
theme_minimal()

# Does humanness_certain predict felt guilt?
m_hc_guilt <- lm(felt_guilt_n ~ humanness_certain_c + cond, data = df)
summary(m_hc_guilt)
##
## Call:
## lm(formula = felt_guilt_n ~ humanness_certain_c + cond, data = df)
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.9405 -0.7965 -0.4726 0.2297 5.5797
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 1.88272 0.10272 18.328 < 2e-16 ***
## humanness_certain_c -0.05697 0.07484 -0.761 0.44729
## condhard -0.42862 0.14956 -2.866 0.00452 **
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1.162 on 243 degrees of freedom
## Multiple R-squared: 0.03333, Adjusted R-squared: 0.02538
## F-statistic: 4.189 on 2 and 243 DF, p-value: 0.01626
# Does humanness_certain predict aggression?
m_hc_intent <- lm(intent_aggression ~ humanness_certain_c + cond, data = df)
m_hc_behav <- lm(total_aggression ~ humanness_certain_c + cond, data = df)
summary(m_hc_intent)
##
## Call:
## lm(formula = intent_aggression ~ humanness_certain_c + cond,
## data = df)
##
## Residuals:
## Min 1Q Median 3Q Max
## -3.3982 -1.0593 -0.1513 1.2434 3.2627
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 4.3365 0.1379 31.444 <2e-16 ***
## humanness_certain_c 0.1039 0.1005 1.034 0.3022
## condhard -0.4939 0.2008 -2.460 0.0146 *
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1.561 on 243 degrees of freedom
## Multiple R-squared: 0.03139, Adjusted R-squared: 0.02342
## F-statistic: 3.938 on 2 and 243 DF, p-value: 0.02075
summary(m_hc_behav)
##
## Call:
## lm(formula = total_aggression ~ humanness_certain_c + cond, data = df)
##
## Residuals:
## Min 1Q Median 3Q Max
## -11.342 -5.129 -0.848 3.811 39.514
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 10.2308 0.6252 16.365 <2e-16 ***
## humanness_certain_c 0.7343 0.4555 1.612 0.108
## condhard -0.2973 0.9102 -0.327 0.744
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 7.074 on 243 degrees of freedom
## Multiple R-squared: 0.01172, Adjusted R-squared: 0.003587
## F-statistic: 1.441 on 2 and 243 DF, p-value: 0.2387
# Does GP moderate the humanness_certain -> felt guilt path?
m_hc_gp <- lm(felt_guilt_n ~ humanness_certain_c * gp_c + cond, data = df)
summary(m_hc_gp)
##
## Call:
## lm(formula = felt_guilt_n ~ humanness_certain_c * gp_c + cond,
## data = df)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1.2192 -0.7470 -0.3960 0.2514 5.5096
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 1.86408 0.10289 18.118 < 2e-16 ***
## humanness_certain_c -0.05024 0.07455 -0.674 0.50102
## gp_c 0.13041 0.07411 1.760 0.07971 .
## condhard -0.40080 0.14976 -2.676 0.00796 **
## humanness_certain_c:gp_c -0.09896 0.07206 -1.373 0.17092
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1.156 on 241 degrees of freedom
## Multiple R-squared: 0.05185, Adjusted R-squared: 0.03611
## F-statistic: 3.295 on 4 and 241 DF, p-value: 0.01185
interact_plot(m_hc_gp,
pred = humanness_certain_c,
modx = gp_c,
modx.values = c(-1, 0, 1),
modx.labels = c("Low GP (-1 SD)", "Mean GP", "High GP (+1 SD)"),
x.label = "Humanness x Confidence (centered)",
y.label = "Felt Guilt",
main.title = "GP Moderates Belief Certainty -> Felt Guilt",
legend.main = "Guilt Proneness") +
theme_minimal()

Believing you’re playing real people doesn’t amplify guilt, even
among those dispositionally prone to it — condition (who’s winning) is
what drives guilt, not perception of opponents.
11. Sadism, Narcissism, and Self-Esteem
11.1 Descriptives and Intercorrelations
df %>%
summarise(
Sadism_M = round(mean(sadism, na.rm = TRUE), 2),
Sadism_SD = round(sd(sadism, na.rm = TRUE), 2),
Narciss_M = round(mean(as.numeric(narcissism), na.rm = TRUE), 2),
Narciss_SD = round(sd(as.numeric(narcissism), na.rm = TRUE), 2),
SelfEst_M = round(mean(as.numeric(selfesteem), na.rm = TRUE), 2),
SelfEst_SD = round(sd(as.numeric(selfesteem), na.rm = TRUE), 2),
GP_M = round(mean(gp, na.rm = TRUE), 2),
GP_SD = round(sd(gp, na.rm = TRUE), 2)
) %>%
kable(caption = "Personality trait descriptives") %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Personality trait descriptives
|
Sadism_M
|
Sadism_SD
|
Narciss_M
|
Narciss_SD
|
SelfEst_M
|
SelfEst_SD
|
GP_M
|
GP_SD
|
|
1.63
|
0.86
|
1.59
|
1.08
|
4.45
|
1.66
|
3.99
|
0.76
|
# Intercorrelation table
trait_cors <- df %>%
dplyr::select(gp, sadism, selfesteem, narcissism) %>%
mutate(across(everything(), as.numeric))
names(trait_cors) <- c("GP", "Sadism", "Self-Esteem", "Narcissism")
cor(trait_cors, use = "pairwise.complete.obs") %>%
round(2) %>%
kable(caption = "Intercorrelations among personality traits") %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Intercorrelations among personality traits
|
|
GP
|
Sadism
|
Self-Esteem
|
Narcissism
|
|
GP
|
1.00
|
-0.31
|
0.16
|
-0.07
|
|
Sadism
|
-0.31
|
1.00
|
-0.10
|
0.43
|
|
Self-Esteem
|
0.16
|
-0.10
|
1.00
|
-0.12
|
|
Narcissism
|
-0.07
|
0.43
|
-0.12
|
1.00
|
11.2 Sadism × Condition → Aggression
Sadism should predict higher aggression, especially when winning
(easy mode).
m_sad_agg_intent <- lm(intent_aggression ~ cond * sadism_c, data = df)
m_sad_agg_behav <- lm(total_aggression ~ cond * sadism_c, data = df)
m_sad_stuns <- lm(total_stuns ~ cond * sadism_c, data = df)
summary(m_sad_agg_intent)
##
## Call:
## lm(formula = intent_aggression ~ cond * sadism_c, data = df)
##
## Residuals:
## Min 1Q Median 3Q Max
## -3.3582 -1.0670 -0.1991 1.1503 3.3009
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 4.349714 0.138488 31.409 < 2e-16 ***
## condhard -0.538233 0.200739 -2.681 0.00784 **
## sadism_c 0.008474 0.150156 0.056 0.95504
## condhard:sadism_c 0.145700 0.202020 0.721 0.47147
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1.563 on 242 degrees of freedom
## Multiple R-squared: 0.03235, Adjusted R-squared: 0.02035
## F-statistic: 2.697 on 3 and 242 DF, p-value: 0.04656
summary(m_sad_agg_behav)
##
## Call:
## lm(formula = total_aggression ~ cond * sadism_c, data = df)
##
## Residuals:
## Min 1Q Median 3Q Max
## -10.694 -5.263 -1.137 3.891 38.894
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 10.3528 0.6302 16.427 <2e-16 ***
## condhard -0.4578 0.9135 -0.501 0.617
## sadism_c 0.3386 0.6833 0.495 0.621
## condhard:sadism_c -0.8427 0.9193 -0.917 0.360
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 7.113 on 242 degrees of freedom
## Multiple R-squared: 0.004922, Adjusted R-squared: -0.007413
## F-statistic: 0.399 on 3 and 242 DF, p-value: 0.7538
summary(m_sad_stuns)
##
## Call:
## lm(formula = total_stuns ~ cond * sadism_c, data = df)
##
## Residuals:
## Min 1Q Median 3Q Max
## -8.207 -4.172 -0.959 3.335 39.335
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 7.8927 0.5325 14.823 <2e-16 ***
## condhard -1.7216 0.7718 -2.231 0.0266 *
## sadism_c 0.3121 0.5773 0.541 0.5892
## condhard:sadism_c -0.3139 0.7767 -0.404 0.6865
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 6.009 on 242 degrees of freedom
## Multiple R-squared: 0.02081, Adjusted R-squared: 0.008667
## F-statistic: 1.714 on 3 and 242 DF, p-value: 0.1647
interact_plot(m_sad_agg_intent,
pred = cond,
modx = sadism_c,
modx.values = c(-1, 0, 1),
modx.labels = c("Low Sadism (−1 SD)", "Mean Sadism", "High Sadism (+1 SD)"),
x.label = "Condition",
y.label = "Intent to Aggress",
main.title = "Sadism × Condition on Intent to Aggress",
legend.main = "Everyday Sadism") +
theme_minimal()

interact_plot(m_sad_stuns,
pred = cond,
modx = sadism_c,
modx.values = c(-1, 0, 1),
modx.labels = c("Low Sadism (−1 SD)", "Mean Sadism", "High Sadism (+1 SD)"),
x.label = "Condition",
y.label = "Total Stuns",
main.title = "Sadism × Condition on Stuns",
legend.main = "Everyday Sadism") +
theme_minimal()

11.3 Sadism × Condition → Felt Guilt
Sadists may feel less guilt after aggressing, especially against
perceived real opponents.
m_sad_guilt <- lm(as.numeric(felt_guilt) ~ cond * sadism_c, data = df)
summary(m_sad_guilt)
##
## Call:
## lm(formula = as.numeric(felt_guilt) ~ cond * sadism_c, data = df)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1.2790 -0.8257 -0.3436 0.1743 4.9968
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 1.88428 0.10261 18.363 < 2e-16 ***
## condhard -0.44200 0.14874 -2.972 0.00326 **
## sadism_c 0.08037 0.11126 0.722 0.47079
## condhard:sadism_c 0.08857 0.14969 0.592 0.55461
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1.158 on 242 degrees of freedom
## Multiple R-squared: 0.04433, Adjusted R-squared: 0.03248
## F-statistic: 3.741 on 3 and 242 DF, p-value: 0.01176
interact_plot(m_sad_guilt,
pred = cond,
modx = sadism_c,
modx.values = c(-1, 0, 1),
modx.labels = c("Low Sadism (−1 SD)", "Mean Sadism", "High Sadism (+1 SD)"),
x.label = "Condition",
y.label = "Felt Guilt",
main.title = "Sadism × Condition on Felt Guilt",
legend.main = "Everyday Sadism") +
theme_minimal()

11.4 Narcissism × Condition → Aggression
m_narc_intent <- lm(intent_aggression ~ cond * narcissism_c, data = df)
m_narc_behav <- lm(total_aggression ~ cond * narcissism_c, data = df)
summary(m_narc_intent)
##
## Call:
## lm(formula = intent_aggression ~ cond * narcissism_c, data = df)
##
## Residuals:
## Min 1Q Median 3Q Max
## -3.3615 -0.8885 -0.2195 1.1677 3.2805
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 4.34423 0.14005 31.019 < 2e-16 ***
## condhard -0.52473 0.20206 -2.597 0.00998 **
## narcissism_c -0.03146 0.20071 -0.157 0.87559
## condhard:narcissism_c 0.21348 0.23209 0.920 0.35858
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1.555 on 241 degrees of freedom
## (1 observation deleted due to missingness)
## Multiple R-squared: 0.03511, Adjusted R-squared: 0.0231
## F-statistic: 2.923 on 3 and 241 DF, p-value: 0.03462
summary(m_narc_behav)
##
## Call:
## lm(formula = total_aggression ~ cond * narcissism_c, data = df)
##
## Residuals:
## Min 1Q Median 3Q Max
## -13.176 -4.681 -1.351 3.649 39.475
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 10.6057 0.6294 16.851 < 2e-16 ***
## condhard -0.5169 0.9080 -0.569 0.56975
## narcissism_c 1.9664 0.9020 2.180 0.03022 *
## condhard:narcissism_c -3.0416 1.0430 -2.916 0.00388 **
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 6.989 on 241 degrees of freedom
## (1 observation deleted due to missingness)
## Multiple R-squared: 0.03666, Adjusted R-squared: 0.02467
## F-statistic: 3.058 on 3 and 241 DF, p-value: 0.02901
interact_plot(m_narc_intent,
pred = cond,
modx = narcissism_c,
modx.values = c(-1, 0, 1),
modx.labels = c("Low Narc. (−1 SD)", "Mean Narc.", "High Narc. (+1 SD)"),
x.label = "Condition",
y.label = "Intent to Aggress",
main.title = "Narcissism × Condition on Intent to Aggress",
legend.main = "Narcissism") +
theme_minimal()

11.5 Narcissism × Condition → Felt Guilt
m_narc_guilt <- lm(as.numeric(felt_guilt) ~ cond * narcissism_c, data = df)
summary(m_narc_guilt)
##
## Call:
## lm(formula = as.numeric(felt_guilt) ~ cond * narcissism_c, data = df)
##
## Residuals:
## Min 1Q Median 3Q Max
## -2.1758 -0.5960 -0.4077 0.4040 5.4416
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 1.9777 0.1003 19.727 < 2e-16 ***
## condhard -0.5254 0.1446 -3.632 0.000343 ***
## narcissism_c 0.6948 0.1437 4.836 2.36e-06 ***
## condhard:narcissism_c -0.6137 0.1661 -3.694 0.000273 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1.113 on 241 degrees of freedom
## (1 observation deleted due to missingness)
## Multiple R-squared: 0.1193, Adjusted R-squared: 0.1083
## F-statistic: 10.88 on 3 and 241 DF, p-value: 9.983e-07
interact_plot(m_narc_guilt,
pred = cond,
modx = narcissism_c,
modx.values = c(-1, 0, 1),
modx.labels = c("Low Narc. (−1 SD)", "Mean Narc.", "High Narc. (+1 SD)"),
x.label = "Condition",
y.label = "Felt Guilt",
main.title = "Narcissism × Condition on Felt Guilt",
legend.main = "Narcissism") +
theme_minimal()

11.6 Self-Esteem × Condition → Aggression
m_se_intent <- lm(intent_aggression ~ cond * selfesteem_c, data = df)
m_se_behav <- lm(total_aggression ~ cond * selfesteem_c, data = df)
summary(m_se_intent)
##
## Call:
## lm(formula = intent_aggression ~ cond * selfesteem_c, data = df)
##
## Residuals:
## Min 1Q Median 3Q Max
## -3.4037 -1.0554 -0.1063 1.1305 3.4446
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 4.35423 0.13803 31.546 <2e-16 ***
## condhard -0.50426 0.20026 -2.518 0.0124 *
## selfesteem_c -0.05678 0.13712 -0.414 0.6791
## condhard:selfesteem_c 0.25658 0.20089 1.277 0.2027
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1.561 on 242 degrees of freedom
## Multiple R-squared: 0.0352, Adjusted R-squared: 0.02324
## F-statistic: 2.943 on 3 and 242 DF, p-value: 0.03372
summary(m_se_behav)
##
## Call:
## lm(formula = total_aggression ~ cond * selfesteem_c, data = df)
##
## Residuals:
## Min 1Q Median 3Q Max
## -11.180 -5.643 -0.977 3.895 36.745
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 10.4025 0.6248 16.648 <2e-16 ***
## condhard -0.4632 0.9066 -0.511 0.6099
## selfesteem_c -0.8920 0.6207 -1.437 0.1520
## condhard:selfesteem_c 1.8635 0.9094 2.049 0.0415 *
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 7.065 on 242 degrees of freedom
## Multiple R-squared: 0.0182, Adjusted R-squared: 0.006025
## F-statistic: 1.495 on 3 and 242 DF, p-value: 0.2165
interact_plot(m_se_intent,
pred = cond,
modx = selfesteem_c,
modx.values = c(-1, 0, 1),
modx.labels = c("Low SE (−1 SD)", "Mean SE", "High SE (+1 SD)"),
x.label = "Condition",
y.label = "Intent to Aggress",
main.title = "Self-Esteem × Condition on Intent to Aggress",
legend.main = "Self-Esteem") +
theme_minimal()

11.7 Personality Trait × Belief: Does Sadism Amplify Dehumanization
Effects?
Sadists who know they’re playing bots may aggress more freely (no
moral constraint); those who think they’re playing real people may
actually enjoy it more.
m_sad_pob_agg <- lm(intent_aggression ~ humanness * sadism_c + cond, data = df)
summary(m_sad_pob_agg)
##
## Call:
## lm(formula = intent_aggression ~ humanness * sadism_c + cond,
## data = df)
##
## Residuals:
## Min 1Q Median 3Q Max
## -3.4333 -1.1697 -0.1801 1.1619 3.2336
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 4.29455 0.15893 27.021 <2e-16 ***
## humanness 0.10011 0.12231 0.819 0.414
## sadism_c 0.10080 0.12575 0.802 0.424
## condhard -0.52754 0.20322 -2.596 0.010 *
## humanness:sadism_c -0.01056 0.11611 -0.091 0.928
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1.566 on 241 degrees of freedom
## Multiple R-squared: 0.03302, Adjusted R-squared: 0.01697
## F-statistic: 2.058 on 4 and 241 DF, p-value: 0.08705
interact_plot(m_sad_pob_agg,
pred = humanness,
modx = sadism_c,
modx.values = c(-1, 0, 1),
modx.labels = c("Low Sadism (−1 SD)", "Mean Sadism", "High Sadism (+1 SD)"),
x.label = "Humanness of Opponents",
y.label = "Intent to Aggress",
main.title = "Sadism × Humanness Belief on Intent to Aggress",
legend.main = "Everyday Sadism") +
theme_minimal()

11.8 Summary Comparison Table: Moderator Interactions on
Aggression
# Collect key interaction terms across models for quick comparison
make_row <- function(mod, label, term) {
coefs <- summary(mod)$coefficients
if (!term %in% rownames(coefs)) return(tibble(Model = label, b = NA, p = NA))
tibble(Model = label,
b = round(coefs[term, "Estimate"], 3),
p = round(coefs[term, "Pr(>|t|)"], 3))
}
bind_rows(
make_row(m_gp_intent, "GP × Cond → Intent Aggress", "condhard:gp_c"),
make_row(m_gp_agg, "GP × Cond → Total Aggression", "condhard:gp_c"),
make_row(m_gp_stuns, "GP × Cond → Stuns", "condhard:gp_c"),
make_row(m_gp_raids, "GP × Cond → Raids", "condhard:gp_c"),
make_row(m_gp_fglt, "GP × Cond → Felt Guilt", "condhard:gp_c"),
make_row(m_gp_out, "GP × Cond → Moral Outrage", "condhard:gp_c"),
make_row(m_sad_agg_intent,"Sadism × Cond → Intent Aggress", "condhard:sadism_c"),
make_row(m_sad_agg_behav, "Sadism × Cond → Total Aggression", "condhard:sadism_c"),
make_row(m_sad_stuns, "Sadism × Cond → Stuns", "condhard:sadism_c"),
make_row(m_sad_guilt, "Sadism × Cond → Felt Guilt", "condhard:sadism_c"),
make_row(m_narc_intent, "Narcissism × Cond → Intent Aggress", "condhard:narcissism_c"),
make_row(m_narc_behav, "Narcissism × Cond → Total Aggression","condhard:narcissism_c"),
make_row(m_narc_guilt, "Narcissism × Cond → Felt Guilt", "condhard:narcissism_c"),
make_row(m_se_intent, "Self-Esteem × Cond → Intent Aggress","condhard:selfesteem_c"),
make_row(m_se_behav, "Self-Esteem × Cond → Total Aggression","condhard:selfesteem_c")
) %>%
kable(col.names = c("Model", "β (interaction)", "p"),
caption = "Personality moderator interaction terms across key models") %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Personality moderator interaction terms across key models
|
Model
|
β (interaction)
|
p
|
|
GP × Cond → Intent Aggress
|
-0.076
|
0.711
|
|
GP × Cond → Total Aggression
|
0.630
|
0.499
|
|
GP × Cond → Stuns
|
0.687
|
0.382
|
|
GP × Cond → Raids
|
-0.058
|
0.875
|
|
GP × Cond → Felt Guilt
|
0.094
|
0.537
|
|
GP × Cond → Moral Outrage
|
-0.148
|
0.554
|
|
Sadism × Cond → Intent Aggress
|
0.146
|
0.471
|
|
Sadism × Cond → Total Aggression
|
-0.843
|
0.360
|
|
Sadism × Cond → Stuns
|
-0.314
|
0.687
|
|
Sadism × Cond → Felt Guilt
|
0.089
|
0.555
|
|
Narcissism × Cond → Intent Aggress
|
0.213
|
0.359
|
|
Narcissism × Cond → Total Aggression
|
-3.042
|
0.004
|
|
Narcissism × Cond → Felt Guilt
|
-0.614
|
0.000
|
|
Self-Esteem × Cond → Intent Aggress
|
0.257
|
0.203
|
|
Self-Esteem × Cond → Total Aggression
|
1.864
|
0.042
|
12. Behavioral Outcomes by Condition
behavioral_summary <- df %>%
group_by(cond) %>%
summarise(
Stuns_M = round(mean(total_stuns, na.rm = TRUE), 2),
Stuns_SD = round(sd(total_stuns, na.rm = TRUE), 2),
Raids_M = round(mean(as.numeric(Player_raids_hut), na.rm = TRUE), 2),
Raids_SD = round(sd(as.numeric(Player_raids_hut), na.rm = TRUE), 2),
Fruit_M = round(mean(as.numeric(Player_produces_fruit), na.rm = TRUE), 2),
Fruit_SD = round(sd(as.numeric(Player_produces_fruit), na.rm = TRUE), 2)
)
kable(behavioral_summary,
col.names = c("Condition",
"Stuns M", "Stuns SD",
"Raids M", "Raids SD",
"Fruit M", "Fruit SD"),
caption = "Behavioral outcomes by condition") %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Behavioral outcomes by condition
|
Condition
|
Stuns M
|
Stuns SD
|
Raids M
|
Raids SD
|
Fruit M
|
Fruit SD
|
|
easy
|
7.86
|
6.93
|
2.46
|
2.23
|
3.95
|
2.09
|
|
hard
|
6.17
|
4.73
|
3.67
|
3.27
|
3.68
|
2.30
|
cat("--- Stuns ---\n"); print(t.test(total_stuns ~ cond, data = df))
## --- Stuns ---
##
## Welch Two Sample t-test
##
## data: total_stuns by cond
## t = 2.2498, df = 227.05, p-value = 0.02542
## alternative hypothesis: true difference in means between group easy and group hard is not equal to 0
## 95 percent confidence interval:
## 0.2097575 3.1692924
## sample estimates:
## mean in group easy mean in group hard
## 7.860465 6.170940
cat("--- Raids ---\n"); print(t.test(as.numeric(Player_raids_hut) ~ cond, data = df))
## --- Raids ---
##
## Welch Two Sample t-test
##
## data: as.numeric(Player_raids_hut) by cond
## t = -3.3536, df = 201.8, p-value = 0.0009526
## alternative hypothesis: true difference in means between group easy and group hard is not equal to 0
## 95 percent confidence interval:
## -1.9203239 -0.4982808
## sample estimates:
## mean in group easy mean in group hard
## 2.457364 3.666667
cat("--- Fruit ---\n"); print(t.test(as.numeric(Player_produces_fruit) ~ cond, data = df))
## --- Fruit ---
##
## Welch Two Sample t-test
##
## data: as.numeric(Player_produces_fruit) by cond
## t = 0.96204, df = 234.9, p-value = 0.337
## alternative hypothesis: true difference in means between group easy and group hard is not equal to 0
## 95 percent confidence interval:
## -0.2834698 0.8245153
## sample estimates:
## mean in group easy mean in group hard
## 3.945736 3.675214
# Figure: all three outcomes
df %>%
mutate(Stuns = total_stuns,
Raids = as.numeric(Player_raids_hut),
Fruit = as.numeric(Player_produces_fruit)) %>%
pivot_longer(c(Stuns, Raids, Fruit), names_to = "Behavior", values_to = "Count") %>%
group_by(cond, Behavior) %>%
summarise(M = mean(Count, na.rm = TRUE),
SE = sd(Count, na.rm = TRUE) / sqrt(n()),
.groups = "drop") %>%
mutate(Behavior = factor(Behavior, levels = c("Stuns", "Raids", "Fruit"))) %>%
ggplot(aes(x = Behavior, y = M, fill = cond)) +
geom_col(position = position_dodge(0.6), width = 0.5) +
geom_errorbar(aes(ymin = M - SE, ymax = M + SE),
position = position_dodge(0.6), width = 0.2) +
scale_fill_manual(values = c("easy" = "#5B8DB8", "hard" = "#E07B54"),
name = "Condition") +
labs(title = "Behavioral Outcomes by Condition",
subtitle = "Error bars = ±1 SE",
x = NULL, y = "Mean Count") +
theme_minimal() +
theme(legend.position = "top")

13. Subjective Intent by Condition
intent_vars <- c("intent_steal", "intent_protect", "intent_stun",
"intent_others_score", "intent_produce")
df %>%
dplyr::select(cond, all_of(intent_vars)) %>%
mutate(across(-cond, as.numeric)) %>%
pivot_longer(-cond, names_to = "Intent", values_to = "Score") %>%
mutate(Intent = recode(Intent,
"intent_steal" = "Steal",
"intent_protect" = "Protect",
"intent_stun" = "Stun",
"intent_others_score" = "Let score",
"intent_produce" = "Produce")) %>%
group_by(cond, Intent) %>%
summarise(M = mean(Score, na.rm = TRUE),
SE = sd(Score, na.rm = TRUE) / sqrt(n()), .groups = "drop") %>%
ggplot(aes(x = Intent, y = M, fill = cond)) +
geom_col(position = position_dodge(0.6), width = 0.5) +
geom_errorbar(aes(ymin = M - SE, ymax = M + SE),
position = position_dodge(0.6), width = 0.2) +
scale_fill_manual(values = c("easy" = "#5B8DB8", "hard" = "#E07B54"),
name = "Condition") +
scale_y_continuous(limits = c(0, 7)) +
labs(title = "Subjective Behavioral Intent by Condition",
x = NULL, y = "Mean (1–7)") +
theme_minimal()

# t-tests
cat("--- Intent Steal ---\n"); print(t.test(as.numeric(intent_steal) ~ cond, data = df))
## --- Intent Steal ---
##
## Welch Two Sample t-test
##
## data: as.numeric(intent_steal) by cond
## t = 2.1697, df = 240.96, p-value = 0.03101
## alternative hypothesis: true difference in means between group easy and group hard is not equal to 0
## 95 percent confidence interval:
## 0.0469693 0.9731062
## sample estimates:
## mean in group easy mean in group hard
## 3.860465 3.350427
cat("--- Intent Stun ---\n"); print(t.test(as.numeric(intent_stun) ~ cond, data = df))
## --- Intent Stun ---
##
## Welch Two Sample t-test
##
## data: as.numeric(intent_stun) by cond
## t = 2.1878, df = 241.43, p-value = 0.02964
## alternative hypothesis: true difference in means between group easy and group hard is not equal to 0
## 95 percent confidence interval:
## 0.05275598 1.00627801
## sample estimates:
## mean in group easy mean in group hard
## 4.837209 4.307692
13.1 Intent vs. Actual Behavior
df %>%
mutate(across(c(intent_steal, intent_stun, intent_produce), as.numeric)) %>%
group_by(cond) %>%
summarise(
r_steal = round(cor(intent_steal, as.numeric(Player_raids_hut), use = "complete.obs"), 2),
r_stun = round(cor(intent_stun, total_stuns, use = "complete.obs"), 2),
r_produce = round(cor(intent_produce, as.numeric(Player_produces_fruit), use = "complete.obs"), 2)
) %>%
kable(col.names = c("Condition",
"r(Intent Steal, Raids)",
"r(Intent Stun, Stuns)",
"r(Intent Produce, Fruit)"),
caption = "Intent-behavior correlations by condition") %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Intent-behavior correlations by condition
|
Condition
|
r(Intent Steal, Raids)
|
r(Intent Stun, Stuns)
|
r(Intent Produce, Fruit)
|
|
easy
|
0.46
|
0.55
|
0.40
|
|
hard
|
0.43
|
0.50
|
0.52
|
14. Play Again
m_play <- lm(as.numeric(play_again) ~ cond, data = df)
summary(m_play)
##
## Call:
## lm(formula = as.numeric(play_again) ~ cond, data = df)
##
## Residuals:
## Min 1Q Median 3Q Max
## -2.8992 -1.2821 -0.2821 1.1008 4.7179
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 3.8992 0.1696 22.991 < 2e-16 ***
## condhard -1.6172 0.2459 -6.576 2.92e-10 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1.926 on 244 degrees of freedom
## Multiple R-squared: 0.1505, Adjusted R-squared: 0.1471
## F-statistic: 43.24 on 1 and 244 DF, p-value: 2.916e-10
df %>%
group_by(cond) %>%
summarise(M = round(mean(as.numeric(play_again), na.rm = TRUE), 2),
SD = round(sd(as.numeric(play_again), na.rm = TRUE), 2)) %>%
kable(col.names = c("Condition", "M", "SD"),
caption = "Desire to play again by condition (1–7)") %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Desire to play again by condition (1–7)
|
Condition
|
M
|
SD
|
|
easy
|
3.90
|
2.12
|
|
hard
|
2.28
|
1.69
|
# Does felt guilt predict not wanting to play again?
m_guilt_play <- lm(as.numeric(play_again) ~ as.numeric(felt_guilt) + cond, data = df)
summary(m_guilt_play)
##
## Call:
## lm(formula = as.numeric(play_again) ~ as.numeric(felt_guilt) +
## cond, data = df)
##
## Residuals:
## Min 1Q Median 3Q Max
## -2.9733 -1.3211 -0.3211 1.3228 4.6789
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 4.05794 0.26182 15.499 < 2e-16 ***
## as.numeric(felt_guilt) -0.08461 0.10626 -0.796 0.427
## condhard -1.65224 0.25002 -6.608 2.44e-10 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1.928 on 243 degrees of freedom
## Multiple R-squared: 0.1528, Adjusted R-squared: 0.1458
## F-statistic: 21.91 on 2 and 243 DF, p-value: 1.791e-09
# Does GP predict wanting to play again?
m_gp_play <- lm(as.numeric(play_again) ~ gp_c + cond, data = df)
summary(m_gp_play)
##
## Call:
## lm(formula = as.numeric(play_again) ~ gp_c + cond, data = df)
##
## Residuals:
## Min 1Q Median 3Q Max
## -2.9264 -1.2933 -0.2825 1.1509 4.7338
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 3.89905 0.16994 22.943 < 2e-16 ***
## gp_c 0.02075 0.12332 0.168 0.867
## condhard -1.61680 0.24643 -6.561 3.2e-10 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1.93 on 243 degrees of freedom
## Multiple R-squared: 0.1506, Adjusted R-squared: 0.1437
## F-statistic: 21.55 on 2 and 243 DF, p-value: 2.424e-09
15. Session Info
sessionInfo()
## R version 4.6.0 (2026-04-24)
## Platform: aarch64-apple-darwin23
## Running under: macOS Ventura 13.3
##
## Matrix products: default
## BLAS: /Library/Frameworks/R.framework/Versions/4.6/Resources/lib/libRblas.0.dylib
## LAPACK: /Library/Frameworks/R.framework/Versions/4.6/Resources/lib/libRlapack.dylib; LAPACK version 3.12.1
##
## locale:
## [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
##
## time zone: America/New_York
## tzcode source: internal
##
## attached base packages:
## [1] stats graphics grDevices utils datasets methods base
##
## other attached packages:
## [1] lubridate_1.9.5 forcats_1.0.1 stringr_1.6.0 dplyr_1.2.1
## [5] purrr_1.2.2 readr_2.2.0 tidyr_1.3.2 tibble_3.3.1
## [9] tidyverse_2.0.0 gridExtra_2.3 mediation_4.5.1 sandwich_3.1-1
## [13] mvtnorm_1.4-1 Matrix_1.7-5 MASS_7.3-65 interactions_1.2.0
## [17] kableExtra_1.4.0 knitr_1.51 ggplot2_4.0.3 scales_1.4.0
## [21] psych_2.6.5 qualtRics_3.2.2
##
## loaded via a namespace (and not attached):
## [1] Rdpack_2.6.6 mnormt_2.1.2 rlang_1.2.0
## [4] magrittr_2.0.5 furrr_0.4.0 otel_0.2.0
## [7] compiler_4.6.0 systemfonts_1.3.2 vctrs_0.7.3
## [10] pkgconfig_2.0.3 crayon_1.5.3 fastmap_1.2.0
## [13] backports_1.5.1 labeling_0.4.3 pander_0.6.6
## [16] rmarkdown_2.31 tzdb_0.5.0 nloptr_2.2.1
## [19] bit_4.6.0 xfun_0.58 cachem_1.1.0
## [22] jsonlite_2.0.0 broom_1.0.13 parallel_4.6.0
## [25] cluster_2.1.8.2 R6_2.6.1 bslib_0.11.0
## [28] stringi_1.8.7 RColorBrewer_1.1-3 parallelly_1.47.0
## [31] boot_1.3-32 rpart_4.1.27 jquerylib_0.1.4
## [34] Rcpp_1.1.1-1.1 zoo_1.8-15 base64enc_0.1-6
## [37] splines_4.6.0 nnet_7.3-20 timechange_0.4.0
## [40] tidyselect_1.2.1 rstudioapi_0.19.0 yaml_2.3.12
## [43] codetools_0.2-20 sjlabelled_1.2.0 listenv_0.10.1
## [46] lattice_0.22-9 withr_3.0.2 S7_0.2.2
## [49] evaluate_1.0.5 foreign_0.8-91 future_1.70.0
## [52] xml2_1.5.2 lpSolve_5.6.23 jtools_2.3.1
## [55] pillar_1.11.1 checkmate_2.3.4 reformulas_0.4.4
## [58] insight_1.5.1 generics_0.1.4 vroom_1.7.1
## [61] hms_1.1.4 minqa_1.2.8 globals_0.19.1
## [64] glue_1.8.1 Hmisc_5.2-6 tools_4.6.0
## [67] data.table_1.18.4 lme4_2.0-1 grid_4.6.0
## [70] rbibutils_2.4.1 colorspace_2.1-2 nlme_3.1-169
## [73] htmlTable_2.5.0 Formula_1.2-5 cli_3.6.6
## [76] textshaping_1.0.5 viridisLite_0.4.3 svglite_2.2.2
## [79] gtable_0.3.6 broom.mixed_0.2.9.7 sass_0.4.10
## [82] digest_0.6.39 htmlwidgets_1.6.4 farver_2.1.2
## [85] htmltools_0.5.9 lifecycle_1.0.5 bit64_4.8.2