// ---- 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, }); const my_id = 1; // in practice, each player are player 1 // 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.Rect({ x: stage.width() / 2, y: 5 * stage.height() / 6, width: 40, height: 20, fill: 'blue', stroke: 'black', strokeWidth: 4, visible: true, offset: { x: 20, y: 0, } }); // 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(160); food_item_02.rotate(40); next_round_button.on('mousedown', function() { is_finished = true; next_round_button.hide(); }); 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); }); // ---- 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. 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 food_angle_list = [70, 110]; var food_angle_copy = [70, 110]; 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 var have_chosen = false; window.addEventListener('keydown', function(e) { if (["Space", "ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"].indexOf(e.code) > -1) { e.preventDefault(); } var angle = 0; switch (e.keyCode) { case 37: //left arrow if (!have_chosen) { angle = -unit_arc_angle; writeMessage('LEFT ARROW can rotate the table clock wisely.\nPress SPACE to send the token!'); } break; case 39: //right arrow if (!have_chosen) { angle = unit_arc_angle; writeMessage('RIGHT ARROW can rotate the table counter clock wisely.\nPress SPACE to send the token!'); } break; case 32: //space 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) .concat('\nPress ENTER to practice one more trial!') .concat('\npress START (with mouse) when you want to begin the experiment!') ); have_chosen = true; return; } } writeMessage("You didn't choose anyone!"); return; case 13: //enter if(have_chosen === true){ have_chosen = false; } else return; angle = food_angle_copy[0] - food_angle_list[0]; food_lot.rotate(-angle); food_angle_list = food_angle_copy; return; } 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); e.preventDefault(); }, false); // ---- 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(); } // ---- Run game_ready = 'Ready!'; window.addEventListener('DOMContentLoaded', (event) => {});