// ---- The Main script of the task ---- // This script use the Konva library to // use the canvas of HTML5 // ---- Define all elements var width = window.innerWidth; var height = window.innerHeight; // stage var stage = new Konva.Stage({ container: 'container', width: width, height: height, }); // layer var layer = new Konva.Layer(); // table plate var table_plate = new Konva.Circle({ x: stage.width() / 2, y: stage.height() / 2, radius: 70, fill: 'white', stroke: 'black', strokeWidth: 2, }); // food items var food_item_01 = new Konva.Circle({ x: 0, y: 0, radius: 7.5, fill: 'red', stroke: 'black', strokeWidth: 2, offset: { x: 0, y: -55 } }); var food_item_02 = food_item_01.clone(); var food_lot = new Konva.Group({ x: stage.width() / 2, y: stage.height()/2, }); food_lot.add(food_item_01).add(food_item_02); // send button var send_button = new Konva.Rect({ x: stage.width() / 2, y: stage.height() / 2, width: 40, height:20, offset:{ x: 20, y: 0, } }); // send text var SEND_TEXT = new Konva.Text({ x: stage.width()/2 - 20, y: stage.height()/2, fontFamily: 'Calibri', fontSize: 24, text: 'Send', fill: 'black', }); // next round button var next_round_button = new Konva.Circle({ x: stage.width() / 2, y: 4 * stage.height() / 5, radius: 20, fill: 'blue', stroke: 'black', strokeWidth: 4, visible: false, }); // text for debug function writeMessage(message) { text.text(message); } var text = new Konva.Text({ x: 10, y: 10, fontFamily: 'Calibri', fontSize: 24, text: '', fill: 'black', }); // draw player seat var seat_edge = 30; var x01 = stage.width() / 2; var y01 = stage.height() / 2; var x02 = x01 - seat_edge / 2; var y02 = y01 + seat_edge / 2 * Math.sqrt(3); var x03 = x01 + seat_edge / 2; var y03 = y02; var seat = { points: [x01, y01, x02, y02, x03, y03], fill: 'white', stroke: 'black', strokeWidth: 2, closed: true, }; var player_seat = new Konva.Line(seat); var o1_seat = player_seat.clone(); var o2_seat = player_seat.clone(); // There's something wrong with the offset of polygons so we have to draw the seat by hand. var offset_radius = 75; player_seat.setAttr('offset', { x: 0, y: -offset_radius, }); o1_seat.setAttr('offset', { x: seat_edge / 2 + offset_radius * Math.sqrt(3) / 2, y: offset_radius / 2 + seat_edge * Math.sqrt(3) / 2 }); o2_seat.setAttr('offset', { x: -seat_edge / 2 - offset_radius * Math.sqrt(3) / 2, y: offset_radius / 2 + seat_edge * Math.sqrt(3) / 2 }); // add the shape to the layer layer.add(table_plate) .add(next_round_button) .add(send_button) .add(SEND_TEXT) .add(text) .add(player_seat) .add(o1_seat) .add(o2_seat) .add(food_lot); stage.add(layer); // ---- Adjust elements and binding events to elements ---- food_lot.rotate(40); food_item_02.rotate(40); next_round_button.on('mousedown', function() { liveSend({ 'type': 'control', 'msg': 'trial_finished' }); is_finished = true; next_round_button.hide(); }); // ---- Define target moving function is_movable(x, y) { var lower_len_bound = 20; // experiment on this. var vector_length = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)); return vector_length >= lower_len_bound; } function get_angle(x, y) { var angle = Math.atan(y / x) * 180 / Math.PI; if (Math.sign(x) === -1) { Math.sign(y) === 1 ? (angle += 180) : (angle -= 180); } return normalize_angle(angle); } function normalize_angle(angle) { while(Math.abs(angle) > 180) angle -= 360 * Math.sign(angle); if(angle === -180) angle = 180; return angle; } function get_rotate_angle(alpha,beta){ var angle = alpha - beta; angle = unit_arc_angle * Math.round(angle / unit_arc_angle); angle = normalize_angle(angle); // rotate to the positon by smallest angle return angle; } var reference_angle = -90; // the self postion for agent 1. var agent_position_list = [reference_angle, normalize_angle(reference_angle+120), normalize_angle(reference_angle-120)];// agent position counter-clock-wisely var lot_angle_list = [-130,-50, -10,70, 110,-170];//lot angle list, in degree, 6 possible angles, counter-clock-wisely. var lot_index_table = [ // agent 1 as actor, decrease the using of hard code in the later version. [[3,6], [4,5]], // agent 2 as actor [[2,5], [6,1]], // agent 3 as actor [[1,4], [2,3]], ];// in the perspective of agent 1; var food_angle_list = [-130,-170]; var unit_arc_angle = 40; // 3 agents on table, and two posible food positions between each of them, so it would be 120/3 = 40 degree table_plate.on('mousedown', function() {// rotate the table per the mouse position. var mouse_position = stage.getPointerPosition(); var x = mouse_position.x - stage.width() / 2; var y = mouse_position.y - stage.height() / 2; y = -y; // note the handness of the axis! var angle = 0; if (is_movable(x, y)) { angle_01 = get_rotate_angle( get_angle(x, y), food_angle_list[0]); angle_02 = get_rotate_angle( get_angle(x, y), food_angle_list[1]); angle = Math.abs(angle_01) >= Math.abs(angle_02) ? angle_02 : angle_01; } liveSend({'type':'graphic','shape':'angle','angle':angle}); rotate_table_by(angle); food_angle_list[0] = normalize_angle(food_angle_list[0] + angle); food_angle_list[1] = normalize_angle(food_angle_list[1] + angle); }); // table_plate.on('mousemove', function() { // var mouse_position = stage.getPointerPosition(); // writeMessage('x: ' + mouse_position.x + ', y: ' + mouse_position.y); // }); // ---- Define animation var rotation_time_limits = 0.5; // 0.5 seconds for rotating the table. var whole_angle = 180; var angular_speed = 0; var time_needed = 0; var rotate_angle = 0; var cum_angle = 0; var table_rotation = new Konva.Animation(function(frame) { // define our rotation animation var angular_diff = (frame.timeDiff * angular_speed) / 1000; if (frame.time / 1000 <= time_needed) { food_lot.rotate(angular_diff); cum_angle += angular_diff; } else { this.stop(); var angle_pad = rotate_angle - cum_angle; cum_angle = 0; food_lot.rotate(angle_pad); frame.time = 0; } }, layer); function rotate_table_by(angle) { rotate_angle = -angle; angular_speed = Math.sign(-angle) * whole_angle / 0.5; time_needed = rotation_time_limits * Math.abs(angle) / whole_angle; table_rotation.start(); } SEND_TEXT.on('mousedown',function(){ for(var food_angle of food_angle_list){ var index = agent_position_list.indexOf(food_angle); if( index === 0){ writeMessage('Please choose a player other than you!'); return; } if(index > 0){ table_plate.listening(false); SEND_TEXT.listening(false); next_round_button.listening(true); var choice = index + my_id if(choice > 3) choice = choice - 3; writeMessage('You choose player'.concat(choice)); liveSend({ 'type': 'data', 'choice': choice ,'food_angle_list':food_angle_list}); return; } } writeMessage("You didn't choose anyone!"); }); // ---- Define lvieRecv function for processing the data get from server var my_id = 0; var current_actor = 0; var current_position = 0; function liveRecv(data) { switch (data.msg_type) { case 'control': switch (data.msg) { case 'initialization': my_id = data.player_id; //writeMessage('my id is'.concat(my_id.toString())); case 'all_players_finished': current_actor = data.next_actor; current_position = data.next_position; if (current_actor === my_id) { writeMessage('You are the actor, please choose'); table_plate.listening(true); SEND_TEXT.listening(true); } else { writeMessage('You are the receiver, please wait'); table_plate.listening(false); SEND_TEXT.listening(false);; } var food_angle_indices = lot_index_table[current_actor-1][current_position-1]; var food_angle_target = []; for(var idx of food_angle_indices){ food_angle_target.push(lot_angle_list[idx-1]); } var food_item_list = [food_item_01,food_item_02]; for(var i in food_angle_list){ var rotate_angle = food_angle_target[i] - food_angle_list[i] - (my_id-1)*120; food_item_list[i].rotate(-rotate_angle); food_angle_list[i] = normalize_angle(food_angle_target[i] - (my_id-1)*120); } //writeMessage(player_seat.getAttr('points').toString()); break; case 'task_ended': document.getElementById("form").submit(); break; } break; case 'data': switch (data.stage) { case 'send': writeMessage('The actor chose '.concat(data.chosen_player.toString()).concat('. Please press the continue button to go to next round.')); next_round_button.show(); next_round_button.listening(true); break; } break; case 'graphic': switch (data.stage){ case 'send': var angle = data.angle; rotate_table_by(angle); food_angle_list[0] = normalize_angle(food_angle_list[0] + angle); food_angle_list[1] = normalize_angle(food_angle_list[1] + angle); break; } break; } } // ---- Run game_ready = 'Ready!'; window.addEventListener('DOMContentLoaded', (event) => { liveSend({ 'type': 'control', 'msg': 'page_loaded' }); });