// ---- The Main script of the task ---- // This script use the Konva library to // use the canvas of HTML5 // ---- Define all elements var width = $("#form").innerWidth(); var height = $("#form").innerWidth(); // stage var stage = new Konva.Stage({ container: 'container', width: width, height: height, }); var my_id = 1; // in practice, every player is player 1 // layer var layer = new Konva.Layer(); var interface_center = [stage.width() / 2, stage.height() / 4 * 1.6]; // table plate stage.add(layer); var player_seat = new Konva.Group({ x: interface_center[0], y: interface_center[1] - stage.width()/3, name: "seat", }); const img_01 = new Image(); const img_02 = new Image(); const img_03 = new Image(); const img_04 = new Image(); image_list = [img_01,img_02,img_03,img_04]; var oimg_01 = new Image(); var oimg_02 = new Image(); var oimg_03 = new Image(); other_img_list = [oimg_01,oimg_02,oimg_03]; var player_rect = new Konva.Image({ x: 0, y: 0, image: image_list[0], width: stage.width()/6/3, height: stage.width()/6/3, fill:"white", opacity:1, visible: true, stroke: "black", strokeWidth: 0, fillAfterStrokeEnabled: true, }); player_seat.add(player_rect); other_seat_list = new Konva.Group({ x: interface_center[0] - stage.width()/6, y: interface_center[1] - stage.width()/6, }); other_text = ['','','']; var other_seat_01 = player_seat.clone(); other_seat_01.find('Image')[0].setAttr('image',image_list[1]); other_seat_01.setAttr('x', 0) .setAttr('y', 0); var other_seat_02 = other_seat_01.clone(); other_seat_02.find('Image')[0].setAttr('image',image_list[2]); other_seat_02.setAttr('x', stage.width()/6); var other_seat_03 = other_seat_01.clone(); other_seat_03.find('Image')[0].setAttr('image',image_list[3]); other_seat_03.setAttr('x', 2*stage.width()/6); other_seat_list.add(other_seat_01) .add(other_seat_02) .add(other_seat_03); layer.add(player_seat); layer.add(other_seat_list); self_arrow_list = new Konva.Group({ x: interface_center[0] + stage.width()/6/6, y: interface_center[1] - stage.width()/3 + stage.width()/6/3, }); arrow_01 = new Konva.Arrow({ points:[0,0, -stage.width()/6,stage.width()/6 - stage.width()/6/3 - 3], stroke:'black', fill:'black', pointerLength:stage.width()/6/6/2, pointerWidth:5, strokeWidth:1, shadowColor:'black', visible: false, }) arrow_02 = arrow_01.clone() .setAttr('points',[0,0, 0,stage.width()/6 - stage.width()/6/3 - 3]); arrow_03 = arrow_01.clone() .setAttr('points',[0,0, stage.width()/6 ,stage.width()/6 - stage.width()/6/3 - 3]); self_arrow_list.add(arrow_01).add(arrow_02).add(arrow_03); layer.add(self_arrow_list); sep_line = new Konva.Line({ points:[interface_center[0] - stage.width()/3+stage.width()/6/3,interface_center[1]+stage.width()/6/6, interface_center[0] + stage.width()/3,interface_center[1]+stage.width()/6/6], stroke:'black', strokeWidth:3 }) layer.add(sep_line); function writeMessage(text,message) { text.text(message); } var self_table_hint = player_seat.clone() .setAttr('x',interface_center[0] - stage.width()/6/2.5) .setAttr('y',interface_center[1] - stage.width()/6/4) .setAttr('visible',false); layer.add(self_table_hint); var self_table_text = new Konva.Text({ x: interface_center[0] - stage.width()/3+stage.width()/6/3, y: interface_center[1] - stage.width()/6/6, fontFamily: 'Calibri', fontSize: stage.width() / 30, text: "", fill: 'black', }); layer.add(self_table_text); var other_table_text = self_table_text.clone() .setAttr('y',interface_center[1] + stage.width()/6/4); layer.add(other_table_text); var condition_text = self_table_text.clone() .setAttr('y',interface_center[1] - stage.width()/2.6) layer.add(condition_text); writeMessage(condition_text,'') send_other = other_seat_list.clone(); send_other.setAttr('y',interface_center[1] + stage.width()/6); layer.add(send_other); receive_other = other_seat_list.clone(); receive_other.setAttr('y',interface_center[1] + 2*stage.width()/6); layer.add(receive_other); other_arrow_list = self_arrow_list.clone() .setAttr('y', interface_center[1] + stage.width()/6 + stage.width()/6/3) .setAttr('visible',true); other_arrow_list.find('Arrow')[0] .setAttr('points',[-stage.width()/6,0, -stage.width()/6,stage.width()/6 - stage.width()/6/3 - 3]); other_arrow_list.find('Arrow')[2] .setAttr('points',[stage.width()/6,0, stage.width()/6,stage.width()/6 - stage.width()/6/3 - 3]); for(let i = 0;i<3;i++){ other_arrow_list.find('Arrow')[i] .setAttr('visible',true); } layer.add(other_arrow_list); other_arrow_list.setAttr('visible',false); send_other.setAttr('visible',false); receive_other.setAttr('visible',false); // ---- Add event const PROB = 0.4 for(let i = 0;i<3;i++){ other_seat_list.find('.seat')[i].on('mouseover',function(){ other_seat_list.find('Image')[i] .setAttr('shadowBlur',5); self_arrow_list.find('Arrow')[i] .setAttr('visible',true) .setAttr('shadowBlur',5); player_seat.find('Image')[0] .setAttr('shadowBlur',5); writeMessage(self_table_text,"送给玩家"); var other_no = other_no_list[i]; var idx = shuffled_idx[i] self_table_hint.find('Image')[0] .setAttr('image',other_img_list[idx]); self_table_hint.setAttr('visible',true); }) other_seat_list.find('.seat')[i].on('mouseout',function(){ other_seat_list.find('Image')[i] .setAttr('shadowBlur',0); self_arrow_list.find('Arrow')[i] .setAttr('visible',false) .setAttr('shadowBlur',0); player_seat.find('Image')[0] .setAttr('shadowBlur',0); self_table_hint.setAttr('visible',false); writeMessage(self_table_text,""); }) other_seat_list.find('.seat')[i].on('mousedown',function(){ var RT = Date.now() - timer; i_rand = i; if(Math.random() > PROB){ i_rand = [0,1,2].at(Math.floor(Math.random() * 3)); } other_seat_list.find('Image')[i] .setAttr('shadowBlur',0) .setAttr('strokeWidth',5); player_seat.find('Image')[0] .setAttr('shadowBlur',5) .setAttr('strokeWidth',5); self_arrow_list.find('Arrow')[i] .setAttr('shadowBlur',0) writeMessage(self_table_text,"随机中..."); other_seat_list.setAttr('listening',false); setTimeout(() => { player_seat.find('Image')[0] .setAttr('shadowBlur',5) .setAttr('strokeWidth',5) .setAttr('stroke','red'); other_seat_list.find('Image')[i_rand] .setAttr('stroke','red') .setAttr('strokeWidth',5) .setAttr('shadowBlur',5); self_arrow_list.find('Arrow')[i_rand] .setAttr('stroke','red') .setAttr('fill','red') .setAttr('visible',true) .setAttr('shadowBlur',5); var idx = shuffled_idx[i]; var rand_idx = shuffled_idx[i_rand] writeMessage(self_table_text,"你最终送给了"); self_table_hint.find('Image')[0] .setAttr('image',other_img_list[rand_idx]); self_table_hint.setAttr('visible',true); writeMessage(other_table_text,"请等待其他玩家进行选择... "); other_seat_list.setAttr('listening',false); liveSend({ 'type': 'data', 'chosen_index':i, 'choice': other_no_list[idx], 'RT': RT, 'revealed':other_no_list[rand_idx]}); }, 500); }) } var next_round_button= new Konva.Label({ x: interface_center[0] + stage.width()/3, y: interface_center[1] - stage.width()/6/6, opacity: 0.75, visible: false, }); layer.add(next_round_button); next_round_button.add(new Konva.Tag({ fill: 'black', lineJoin: 'round', cornerRadius: 5, shadowColor: 'black', shadowBlur: 0, shadowOffset: 0, shadowOpacity: 0.75 })); next_round_button.add(new Konva.Text({ text: '继续', fontFamily: 'Calibri', fontSize: stage.width()/30, padding: 5, fill: 'white' })); next_round_button.on('mouseout', function() { next_round_button.setAttr('opacity', 0.75); }) next_round_button.on('mouseover', function() { next_round_button.setAttr('opacity', 1); }) next_round_button.on('mousedown', function() { writeMessage(other_table_text,"请等待其他玩家继续实验... "); var CT = Date.now() - timer; liveSend({ 'type': 'control', 'msg': 'trial_finished', 'CT':CT}); is_finished = true; next_round_button.hide(); }) var other_no_list = [2,3,4]; var self_no = 1; var condition = 0; function shuffle(arr) { const a = arr.slice(); for (let i = a.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [a[i], a[j]] = [a[j], a[i]]; } return a; } function recover_state(data){ chosen_player_list = data.chosen_player_list; player_no_list = data.player_no_list; revealed_player_list = data.revealed_player_list; trial_finished_list = data.trial_finished_list; self_idx = 0; for(let i = 0;i < 4;i++){ if(player_no_list[i] == self_no){ self_idx = i; break; } } rewarded_no = chosen_player_list[self_idx]; revealed_no = revealed_player_list[self_idx] if(rewarded_no == 0||revealed_no == 0) return; other_seat_list.setAttr('listening',false); rewarded_idx = 0; revealed_idx = 0; for(let i = 0;i < 3;i++){ idx = shuffled_idx[i]; if(other_no_list[idx] == rewarded_no){ rewarded_idx = i; } if(other_no_list[idx] == revealed_no){ revealed_idx = i } } other_seat_list.find('Image')[rewarded_idx] .setAttr('shadowBlur',0) .setAttr('strokeWidth',5); player_seat.find('Image')[0] .setAttr('shadowBlur',5) .setAttr('strokeWidth',5); self_arrow_list.find('Arrow')[rewarded_idx] .setAttr('stroke','black') .setAttr('fill','black') .setAttr('visible',true) .setAttr('shadowBlur',5); player_seat.find('Image')[0] .setAttr('shadowBlur',5) .setAttr('strokeWidth',5) .setAttr('stroke','red'); other_seat_list.find('Image')[revealed_idx] .setAttr('stroke','red') .setAttr('strokeWidth',5) .setAttr('shadowBlur',5); self_arrow_list.find('Arrow')[revealed_idx] .setAttr('stroke','red') .setAttr('fill','red') .setAttr('visible',true) .setAttr('shadowBlur',5); writeMessage(self_table_text,"你最终送给了"); idx = shuffled_idx[revealed_idx]; self_table_hint.find('Image')[0] .setAttr('image',other_img_list[idx]); self_table_hint.setAttr('visible',true); writeMessage(other_table_text,"请等待其他玩家进行选择... "); self_trial_finished = trial_finished_list[self_idx]; all_chosen = 1; all_finished = 1; for(i = 0;i < 4;i++){ if(trial_finished_list[i] !=2){ all_finished = 0; } if(chosen_player_list[i] == 0||revealed_player_list[i] ==0){ all_chosen = 0; } } if(all_chosen == 0) return; //wait for others to choose. for(let i = 0;i < 4;i++){ chosen_player = data.chosen_player_list[i]; player_no = data.player_no_list[i]; if(player_no == self_no) continue; for(let j=0;j<3;j++){ j_player = other_no_list[shuffled_idx[j]] if(j_player == player_no){ receive_other.find('Image')[j] .setAttr('image',image_list[chosen_player-1]); } } } send_other.setAttr('visible',true); receive_other.setAttr('visible',true); other_arrow_list.setAttr('visible',true); writeMessage(other_table_text,"随机后,其他玩家送给:"); if(self_trial_finished != 2){ timer = Date.now(); next_round_button.show(); next_round_button.listening(true); return; } writeMessage(other_table_text,"请等待其他玩家继续实验... "); } var shuffled_idx = [0,1,2] function liveRecv(data) { switch (data.msg_type) { case 'control': switch (data.msg) { case 'initialization': self_no = data.player_id; condition = data.condition; image_seq = [data.img_01,data.img_02,data.img_03,data.img_04]; for(let i = 0;i < 4;i++){ image_list[i].src = '/static/imgs/image_'+image_seq[i].toString().padStart(4,'0')+'.webp'; } player_seat.find('Image')[0] .setAttr('image',image_list[self_no-1]); for(let i = 0;i<3;i++){ other_no = i + self_no + 1; if(other_no >= 5) other_no = other_no - 4; other_no_list[i] = other_no; other_img_list[i].src = image_list[other_no-1].src } shuffled_idx = shuffle([0,1,2]); for(let i = 0;i<3;i++){ idx = shuffled_idx[i] other_seat_list.find('Image')[i] .setAttr('image',other_img_list[idx]); send_other.find('Image')[i] .setAttr('image',other_img_list[idx]); } if(condition == 1){ condition_text.setAttr("fill","#d66e4a") writeMessage(condition_text,'有奖励'); } if(condition == 0){ condition_text.setAttr("fill","#4f9baf") writeMessage(condition_text,'无奖励'); } recover_state(data); break; case 'all_players_finished': case 'task_ended': document.getElementById("form").submit(); break; } break; case 'data': switch (data.stage) { case 'send': for(let i = 0;i < 4;i++){ chosen_player = data.chosen_player_list[i]; player_no = data.player_no_list[i]; if(player_no == self_no) continue; for(let j=0;j<3;j++){ j_player = other_no_list[shuffled_idx[j]] if(j_player == player_no){ receive_other.find('Image')[j] .setAttr('image',image_list[chosen_player-1]); } } } setTimeout(() => { send_other.setAttr('visible',true); receive_other.setAttr('visible',true); other_arrow_list.setAttr('visible',true); writeMessage(other_table_text,"随机后,其他玩家送给:"); timer = Date.now(); next_round_button.show(); next_round_button.listening(true); }, 500); break; } break; } } layer.draw(); // ---- Run game_ready = 'Ready!'; var timer = 0; window.addEventListener('DOMContentLoaded', (event) => { liveSend({ 'type': 'control', 'msg': 'page_loaded' }); timer = Date.now(); });