首页 > > 详细

INFT 3030讲解、讲解Information Technology、辅导Java语言、Java编程调试 辅导Database|辅导Web开

School of Information Technology and Mathematical Sciences
INFT 3030 Concurrent Programming
Concurrent Programming Assignment SP2 2019
Introduction
This document defines the requirements for the assignment in Concurrent Programming. The assignment is intended to give
you experience in modelling a concurrent requirement and writing a concurrent program in Java to implement it. The
application is a multi-player game server (MPGS).
A MPGS is software that runs on a computer connected to Internet. It enables and handles all communication between players
of games running on Internet connected devices so that they can participate in a shared game. A turn based game is often
played using a multiplayer game server. In these games each player (using client software) takes a turn in giving input to the
game server.
Google (PlayServices) and Apple (GameCenter) provide a MPGS for their mobile platforms. There are also independent
companies providing these services; examples of which are Photon (https://www.exitgames.com/en/OnPremise) and Smartfox
(http://www.smartfoxserver.com/products).
MPGS can quickly become very complex and contain many functions that we won’t be implementing in this assignment. The
scope of the MPGS to be implemented by you and your team in for the Concurrency Programming assignment are the ability
for players to connect and to play a game together, noting the following:
Only players who are registered are allowed to connect to and play on the MPGS. It is acceptable to have a list of
registered players in an external database, i.e. you can assume that the registration is taken care of by a separate piece
of software and the list of registered players is provided by that software. Typically, when a player connects to the
MPGS, they are often placed into a “lobby”.
Normally a player in the lobby would then choose a game to join or start their own and hope others will join. However
in this assignment, you are only required to have one multiplayer game to join and everyone who is logged in will have
to play in that one game.
The game to be hosted by the MPGS will be called multiplayer snakes
Multiplayer snakes
Single player versions of snakes can be found in many places on the internet, e.g. http://playsnake.org
and http://codeincomplete.com/projects/snakes/. The rules vary between different variants:
you can get points for eating fruit, as well as simply surviving as long as you can
it can be OK to cross the edge of the screen, or this can end your game
game time can be limited so you get a fixed time to score, or the game can go on until you cross the edge of the screen
or collide with yourself or another player.
A downloadable multiplayer version of snakes for windows is here: http://sandbox.yoyogames.com/games/172375/download. In
this case there are no food items you just try to block other snakes and need to be the last snake left with somewhere to move
to win. Another example of the idea can be viewed at https://www.youtube.com/watch?v=hRy_qQLdptA although you may not
understand the German commentary!
For the purposes of this assignment, you are free to choose your own rules for the actual snakes game. The main focus for this
assessment is the way your MPGS supports multiple concurrent players.2
Server design
After they have connected and joined a game, players send to the game server their moves. These moves are very simple: Up,
Down, Left, Right or No Move. A No Move could mean either keep going in the same direction as the last move or stop and
wait. The game server takes all the moves from all the players and updates its copy of the game board. If you chose to have
fruit the snakes eat to get points, the server will need to detect if fruit has been eaten and updates player scores. The server
then broadcasts the game board (with all the other players shown and any fruit left or added) to each player so they can make
the next move.
In a fully networked implementation of this game each player will have to display on their screen the current state of play and
then transmit moves to the server (using TCP/IP or UDP sockets) over the internet. Given this can be a time intensive exercise,
in this assignment, we are going to limit the implementation to save on programming time and leave time to concentrate on
the concurrent aspects of the server and players. For the MPGS you will implement, no networking is required. In keeping with
the rest of this course we will restrict the game server and players to be threads all on the same Java process (JVM).
This means that you will not have to write TCP/IP or UDP socket code, rather you will need to manage the communication
between players and the server using Java constructs. This also means that initially at least you can get the server to draw the
game state on a single screen where all the players can see it rather than sending a copy down to each player for them to draw
on their own screen.
Modelling
In addition to writing concurrent Java code for the game server and clients (players), you will need to construct a model of their
game server and players using the LTSA tool. The modelling will need to accurately capture the concurrent behaviour of your
proposed server and show that it is both safe, i.e. it is not likely to freeze. And that it is in fact using multiple threads to perform
its functions in a way than can be scaled up to many different players.
Testing
You will be required to demonstrate testing to show that the game can be played by 4 real players and up to 100 simulated
players. Simulated players just make random moves on the board. It would be expected that you apply testing concepts that
have been discussed in the lectures (JUnit assertions and mock objects).
Assessment summary
For internal students this will be a group project with teams of 4. External students will work alone on the assignment and
hence will not need to provide an as complete a solution as internal students.
The assessment scoring is divided into two stages.
Checkpoint 1 Checkpoint 2
Final Submission and
Presentation Total
Internal 2.5 points scored as a group 2.5 points scored as a group 10 points scored individually
5 points scored as a group 20
External 2.5 2.5 15 20
The following subsections provide details that are focused on internal students. Clarifications for external students are provided
at the end of each section.
Checkpoint 1
This body of work is to be presented in the week 6 lab slot.
The purpose of the check point provides an opportunity for the design of the server to be checked before implementation
commences. At this checkpoint the group should have finalised the concurrency design of the server and player roles and
created a Java class design document. The group should also have a draft LTSA model of their concurrency design. All this is to
be gathered in a design report of approximately 3000 words (or equivalent with diagrams). The report needs to allocate Java
classes to group members for the first sprint to implementation.
External students will do this as individuals. The deliverable is the same, but they do not need to prepare such a large document
(1000 words).3
Checkpoint 2
This body of work is to be presented in the week 8 lab slot.
The purpose of this checkpoint is to evaluate some early working code as planned for the first sprint in checkpoint 1. There
should be an emphasis on a thread-safe monitor controlled buffer between clients and the server and some initial
implementation of the threaded server code especially the thread safe game state code. The LTSA model should be nearing
completion. Some attention should be focused on testing and how this will be performed. Finally plans for the final
implementation should be finalized and every team member should have some coding allocated to complete the assignment.
An updated report will be required for this checkpoint and working code is required.
External students do not need as large an updated report (1000 words) and the scope of code implementation will be reduced.
Final Submission and Presentation
The final submission will provide working code which allows four players to engage in the game and code to allow simulated
play for up to 100 players. There will also be a completed LTSA model of the concurrent behaviour the players and the game
server. The model should demonstrate that the game server is capable of concurrent operation and that it has no potential
freezes (deadlocks). A report will describe the structure of the code for both the model and the actual implementation. The
report should be approximately 5000 words or equivalent diagrams. The group will present the server working with 4 real
players and 100 simulated players. The group mark will be allocated for successful integration of group member’s code and
model and the demonstration/presentation. The individual marks will be allocated to the code each student has written and
their individual contribution to contribution to the model and the report.
External students need only demonstrate one real player, and scaling up to 4 simulated players. External students need only
provide a report of 1000 words. External students can demonstrate via a screen capture or video.4
Technical Specifications
Stage 1
The first stage of the project should be to allow players to login.
Each player will be modelled and coded as a concurrent thread. The server design should be able to handle concurrent login
requests from different players.
Logins must be thread-safe on the server. It is not necessary to support registration. Registering players can be handled by
manually loading their usernames and passwords into a suitable database. A suggested package is MapDB (as presented in
Appendix 1) which is a good choice for a thread-safe embedded database.
It is a requirement for the concurrent login code that a thread-safe standard Java collection is NOT to be used to pass messages
between the player logging in and the server. It is also a requirement that monitors be used to manage access to this collection.
Stage 2
The second stage of the project is to implement the game playing part of the server.
Once the server starts it should be ready to accept logged in players playing the game.
It is a requirement to use thread safe collections for the communication channels between players and the server for game
playing and for the storage of the game state. It is a requirement to use a thread pool for game player interactions based on the
java.util.concurrent.executor model. The actual game playing will adopt the pure client server model.
The server's main loop would be a concurrent version of this pseudo code:
while not done
for each player in world
if input exists
get player command
execute player command
tell player of the results
simulate the world
broadcast to all players
The client's main loop would be like this:
while not done
if player has typed any text
send typed text to server
if output from server exists
print output
In the case of the assignment Java threaded version with no networking the server could take care of updating the screen since
all players can see the single screen.
There is a simple snake single player code example at: https://code.google.com/p/java-snake/source/browse/trunk/javasnake/src/snake/Main.java.
Code from here tested on IntelliJ 14 community edition can be found in Appendix 25
Appendix 1
Code for concurrent persistent hashmaps. See http://www.mapdb.org
You need to load the library for MapDB via Maven
IntelliJ: File->Project Structure->Libraries->+->Browse-> select org.mapdb.1.0.6
import org.mapdb.*;
import java.io.File;
import java.io.Serializable;
import java.util.HashMap;
import java.util.concurrent.ConcurrentNavigableMap;
public class Main {
public static void main(String[] args) {
// configure and open database using builder pattern.
// all options are available with code auto-completion.
DB db = DBMaker.newFileDB(new File("testdb"))
.closeOnJvmShutdown()
.encryptionEnable("password")
.make();
// open existing an collection (or create new)
ConcurrentNavigableMap map = db.getTreeMap("collectionName");
// To simplfy access to the classes stored as an infostore use generics
ConcurrentNavigableMap mymap = db.getTreeMap("myInfoStoreCollection");
// example of ConcurrentNavigableMap taken from
// http://examples.javacodegeeks.com/core-java/util/concurrent/concurrentnavigablemap/java-util-
// concurrent-concurrentnavigablemap-example/
ConcurrentNavigableMap navmap = db.getTreeMap("myNavmap");
navmap.put(1, "Sunday");
navmap.put(2, "Monday");
navmap.put(3, "Tuesday");
navmap.put(4, "Wednesday");
navmap.put(5, "Thursday");
navmap.put(6, "Friday");
navmap.put(7, "Saturday");
System.out.println("1. descendingKeySet(): " + navmap.descendingKeySet() + "\n");
System.out.println("2. descendingMap(): " + navmap.descendingMap() + "\n");
System.out.println("3. headMap(K toKey): " + navmap.headMap(3) + "\n");
System.out.println("4. headMap(K toKey, boolean inclusive): " + navmap.headMap(3, true) + "\n");
System.out.println("5. keySet(): " + navmap.keySet() + "\n");
System.out.println("6. navigableKeySet(): " + navmap.navigableKeySet() + "\n");
System.out.println("7. subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive): "
+ navmap.subMap(3, true, 6, true) + "\n");
System.out.println("8. subMap(K fromKey, K toKey): " + navmap.subMap(3, 6) + "\n");
System.out.println("9. tailMap(K fromKey): " + navmap.tailMap(3) + "\n");
System.out.println("10. tailMap(K fromKey, boolean inclusive): " + navmap.tailMap(3, true) + "\n");
map.put(1, "one");
map.put(2, "two");
// map.keySet() is now [1,2]
System.out.println(map.get(2));
db.commit(); //persist changes into disk
map.put(3, "three");
// map.keySet() is now [1,2,3]
db.rollback(); //revert recent changes
// map.keySet() is now [1,2]
// This was taken from
// http://stackoverflow.com/questions/12099843/storing-a-new-object-as-the-value-of-a-hashmap
HashMap mapper = new HashMap();
//HashMap mapper = new HashMap();
//InfoStor myInfoStore = new InfoStor("NS02");
System.out.println("Trying");
mymap.put("NS02", new InfoStor("NS02"));
System.out.println(mymap.get("NS02").getName());
db.close();
}
}6
// this class must be serializable
class InfoStor implements Serializable {
private String vmName;
private String platform;
private Integer memory;
public InfoStor (String name) {
vmName = name;
}
String getName(){
return vmName;
}
void setPlatform(String p){
platform = p;
}
String getPlatform(){
return platform;
}
void setMemory(Integer m){
memory = m;
}
Integer getMemory(){
return memory;
}
}7
Appendix 2
Simple snake code from https://code.google.com/p/java-snake/source/browse/trunk/java-snake/src/snake/Main.java
package snake;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.awt.image.BufferStrategy;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* @author Peuch
*/
public class Main implements KeyListener, WindowListener {
// KEYS MAP
public final static int UP = 0;
public final static int DOWN = 1;
public final static int LEFT = 2;
public final static int RIGHT = 3;

// GRID CONTENT
public final static int EMPTY = 0;
public final static int FOOD_BONUS = 1;
public final static int FOOD_MALUS = 2;
public final static int BIG_FOOD_BONUS = 3;
public final static int SNAKE = 4;
private int[][] grid = null;
private int[][] snake = null;
private int direction = -1;
private int next_direction = -1;
private int height = 600;
private int width = 600;
private int gameSize = 40;
private long speed = 70;
private Frame frame = null;
private Canvas canvas = null;
private Graphics graph = null;
private BufferStrategy strategy = null;
private boolean game_over = false;
private boolean paused = false;

private int score = 0;
private int grow = 0;

private int seconde,minute,milliseconde = 0; // Clock values
private long cycleTime = 0;
private long sleepTime = 0;
private int bonusTime = 0;
private int malusTime = 0;
/**
* @param args
* the command line arguments
*/
public static void main(String[] args) {
Main game = new Main();
game.init();
game.mainLoop();
}
public Main() {
super();
frame = new Frame();
canvas = new Canvas();
grid = new int[gameSize][gameSize];
snake = new int[gameSize * gameSize][2];
}
public void init() {8
frame.setSize(width + 7, height + 27);
frame.setResizable(false);
frame.setLocationByPlatform(true);
canvas.setSize(width + 7, height + 27);
frame.add(canvas);
canvas.addKeyListener(this);
frame.addWindowListener(this);
frame.dispose();
frame.validate();
frame.setTitle("Snake");
frame.setVisible(true);
canvas.setIgnoreRepaint(true);
canvas.setBackground(Color.WHITE);

canvas.createBufferStrategy(2);

strategy = canvas.getBufferStrategy();
graph = strategy.getDrawGraphics();
initGame();
renderGame();
}
public void mainLoop() {
while (!game_over) {
cycleTime = System.currentTimeMillis();
if(!paused)
{
direction = next_direction;
moveSnake();
}
renderGame();
cycleTime = System.currentTimeMillis() - cycleTime;
sleepTime = speed - cycleTime;
if (sleepTime < 0)
sleepTime = 0;
try {
Thread.sleep(sleepTime);
} catch (InterruptedException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null,
ex);
}
}
}
private void initGame() {
// Initialise tabs
for (int i = 0; i < gameSize; i++) {
for (int j = 0; j < gameSize; j++) {
grid[i][j] = EMPTY;
}
}
for (int i = 0; i < gameSize * gameSize; i++) {
snake[i][0] = -1;
snake[i][1] = -1;
}
snake[0][0] = gameSize/2;
snake[0][1] = gameSize/2;
grid[gameSize/2][gameSize/2] = SNAKE;
placeBonus(FOOD_BONUS);
}
private void renderGame() {
int gridUnit = height / gameSize;
canvas.paint(graph);
do {
do {
graph = strategy.getDrawGraphics();
// Draw Background
graph.setColor(Color.WHITE);
graph.fillRect(0, 0, width, height);
// Draw snake, bonus ...
int gridCase = EMPTY;
for (int i = 0; i < gameSize; i++) {9
for (int j = 0; j < gameSize; j++) {
gridCase = grid[i][j];
switch (gridCase) {
case SNAKE:
graph.setColor(Color.BLUE);
graph.fillOval(i * gridUnit, j * gridUnit,
gridUnit, gridUnit);
break;

case FOOD_BONUS:
graph.setColor(Color.darkGray);
graph.fillOval(i * gridUnit + gridUnit / 4, j
* gridUnit + gridUnit / 4, gridUnit / 2,
gridUnit / 2);
break;
case FOOD_MALUS:
graph.setColor(Color.RED);
graph.fillOval(i * gridUnit + gridUnit / 4, j
* gridUnit + gridUnit / 4, gridUnit / 2,
gridUnit / 2);
break;
case BIG_FOOD_BONUS:
graph.setColor(Color.GREEN);
graph.fillOval(i * gridUnit + gridUnit / 4, j
* gridUnit + gridUnit / 4, gridUnit / 2,
gridUnit / 2);
break;
default:
break;
}
}
}
graph.setFont(new Font(Font.SANS_SERIF, Font.BOLD, height / 40));
if (game_over) {
graph.setColor(Color.RED);
graph.drawString("GAME OVER", height / 2 - 30, height / 2);
graph.drawString("YOUR SCORE : " + score, height / 2 - 40, height / 2 +50);
graph.drawString("YOUR TIME : " + getTime(), height / 2 - 42, height / 2 +100);
}
else if (paused) {
graph.setColor(Color.RED);
graph.drawString("PAUSED", height / 2 - 30, height / 2);
}
graph.setColor(Color.BLACK);
graph.drawString("SCORE = " + score, 10, 20);
graph.drawString("TIME = " + getTime(), 100, 20); //Clock
graph.dispose();
} while (strategy.contentsRestored());
// Draw image from buffer
strategy.show();
Toolkit.getDefaultToolkit().sync();
} while (strategy.contentsLost());
}
private String getTime() {
String temps = new String(minute + ":" + seconde);
if (direction<0 || paused)
return temps;

milliseconde++;
if (milliseconde==14){
seconde++;
milliseconde=0;}
if (seconde==60){
seconde=0;
minute++;
}

return temps;
}

private void moveSnake() {
if (direction < 0) {
return;
}
int ymove = 0;
int xmove = 0;10
switch (direction) {
case UP:
xmove = 0;
ymove = -1;
break;
case DOWN:
xmove = 0;
ymove = 1;
break;
case RIGHT:
xmove = 1;
ymove = 0;
break;
case LEFT:
xmove = -1;
ymove = 0;
break;
default:
xmove = 0;
ymove = 0;
break;
}
int tempx = snake[0][0];
int tempy = snake[0][1];
int fut_x = snake[0][0] + xmove;
int fut_y = snake[0][1] + ymove;

if(fut_x < 0)
fut_x = gameSize - 1;
if(fut_y < 0)
fut_y = gameSize - 1;
if(fut_x >= gameSize)
fut_x = 0;
if(fut_y >= gameSize)
fut_y = 0;

if (grid[fut_x][fut_y] == FOOD_BONUS)
{
grow++;
score++;
placeBonus(FOOD_BONUS);

}
if (grid[fut_x][fut_y] == FOOD_MALUS)
{
grow += 2;
score--;
}
else if(grid[fut_x][fut_y] == BIG_FOOD_BONUS)
{
grow += 3;
score +=3;
}
snake[0][0] = fut_x;
snake[0][1] = fut_y;
if ((grid[snake[0][0]][snake[0][1]] == SNAKE)) {
gameOver();
return;
}
grid[tempx][tempy] = EMPTY;
int snakex, snakey, i;
for (i = 1; i < gameSize * gameSize; i++) {
if ((snake[i][0] < 0) || (snake[i][1] < 0)) {
break;
}
grid[snake[i][0]][snake[i][1]] = EMPTY;
snakex = snake[i][0];
snakey = snake[i][1];
snake[i][0] = tempx;
snake[i][1] = tempy;
tempx = snakex;
tempy = snakey;
}
for (i = 0; i < gameSize * gameSize; i++) {
if ((snake[i][0] < 0) || (snake[i][1] < 0)) {
break;11
}
grid[snake[i][0]][snake[i][1]] = SNAKE;
}
bonusTime--;
if (bonusTime == 0)
{
for (i = 0; i < gameSize; i++)
{
for (int j = 0; j < gameSize; j++)
{
if(grid[i][j]==BIG_FOOD_BONUS)
grid[i][j]=EMPTY;
}
}
}
malusTime --;
if (malusTime == 0)
{
for (i = 0; i < gameSize; i++)
{
for (int j = 0; j < gameSize; j++)
{
if(grid[i][j]==FOOD_MALUS)
grid[i][j]=EMPTY;
}
}
}
if (grow > 0) {
snake[i][0] = tempx;
snake[i][1] = tempy;
grid[snake[i][0]][snake[i][1]] = SNAKE;
if(score%10 == 0)
{
placeBonus(BIG_FOOD_BONUS);
bonusTime = 100;
}
if(score%5 == 0)
{
placeMalus(FOOD_MALUS);
malusTime = 100;
}
grow --;
}
}
private void placeBonus(int bonus_type) {
int x = (int) (Math.random() * 1000) % gameSize;
int y = (int) (Math.random() * 1000) % gameSize;
if (grid[x][y] == EMPTY) {
grid[x][y] = bonus_type;
} else {
placeBonus(bonus_type);
}
}

private void placeMalus(int malus_type) {
int x = (int) (Math.random() * 1000) % gameSize;
int y = (int) (Math.random() * 1000) % gameSize;
if (grid[x][y] == EMPTY) {
grid[x][y] = malus_type;
} else {
placeMalus(malus_type);
}
}
private void gameOver() {
game_over = true;
}
// IMPLEMENTED FUNCTIONS
public void keyPressed(KeyEvent ke) {
int code = ke.getKeyCode();
Dimension dim;
switch (code) {
case KeyEvent.VK_UP:
if (direction != DOWN) {
next_direction = UP;
}
break;12
case KeyEvent.VK_DOWN:
if (direction != UP) {
next_direction = DOWN;
}
break;
case KeyEvent.VK_LEFT:
if (direction != RIGHT) {
next_direction = LEFT;
}
break;
case KeyEvent.VK_RIGHT:
if (direction != LEFT) {
next_direction = RIGHT;
}
break;
case KeyEvent.VK_F11:
dim = Toolkit.getDefaultToolkit().getScreenSize();
if ((height != dim.height - 50) || (width != dim.height - 50)) {
height = dim.height - 50;
width = dim.height - 50;
} else {
height = 600;
width = 600;
}
frame.setSize(width + 7, height + 27);
canvas.setSize(width + 7, height + 27);
canvas.validate();
frame.validate();
break;
case KeyEvent.VK_ESCAPE:
System.exit(0);
break;

case KeyEvent.VK_SPACE:
if(!game_over)
paused = !paused;
break;
default:
// Unsupported key
break;
}
}
public void windowClosing(WindowEvent we) {
System.exit(0);
}
// UNNUSED IMPLEMENTED FUNCTIONS
public void keyTyped(KeyEvent ke) {}
public void keyReleased(KeyEvent ke) {}
public void windowOpened(WindowEvent we) {}
public void windowClosed(WindowEvent we) {}
public void windowIconified(WindowEvent we) {}
public void windowDeiconified(WindowEvent we) {}
public void windowActivated(WindowEvent we) {}
public void windowDeactivated(WindowEvent we) {}
}13
Administrivia
Extensions
Extensions for assignments may be available under the following conditions:
permanent or temporary disability
compassionate grounds
In all cases documentary evidence (e.g. medical certificate, road accident report, obituary) must be provided.
Late penalties
Late assignments that do not have approved extensions will not be accepted.
Academic conduct
Deliberate academic misconduct such as plagiarism is subject to penalties.
You are advised to become familiar with the University's policy available at http://www.unisa.edu.au/policies/manual/.
In an individual assignment or the individual part of a group assignment the work you submit must be entirely your own: no part
of your submission must be anybody else’s work or work that you did together with another student or students.
All use of published material (eg the Web, or a book) must be fully referenced. If you copy text from another source, you must
place it in quotation marks and include a reference to the original source. If you make any use of ideas or information, including
diagrams, from another source, you must reference that source.
Programming code cannot be included in an assignment copied from the web unless it is separately declared at the front of the
assignment. Code obtained from the web will not contribute to your individual or group grade in the assignment. If we find
even one line of code in your assignment that has been copied from the web and not declared you may receive zero for the
whole assignment. We will also check this assignment against previously submitted assignments and if we find any identical
code or sentences you may also get a mark of zero. If you get a mark of zero you may also be reported for plagiarism which
could lead to further consequences which are outlined in University policy. Please read the material on this page if you are not
sure.
There will be questions asked during presentation about the assignments which you may be unable to answer if you do not do
the assignment yourself.
All use of outside assistance – e.g. essay farms on the Web or work written for you by a friend – is strictly forbidden and will
attract a minimum penalty of zero for the assignment and may risk your expulsion from the university.
All the requirements documents for this assignment (including this one) are copyright to the University of South Australia. You
are not authorized to reproduce these requirements documents in any form nor submit or post or transmit them to any web
site. In particular posting these requirements on a web site which is aimed to solicit other people to do the assignment for a fee
is a breach of copyright and could be referred to the law enforcement authorities.
To defend yourself in the case of any suspicion of academic misconduct, you are strongly urged to retain all evidence of how
you developed your assignment, such as rough work sheets, notes, drafts, copies of reference material, minutes of meetings
etc.
You are free to discuss the assignment with others, and to give and receive help, including references and general discussion of
the main arguments and conclusions, as long as your part of the program code and text of your part of a report is written only
by yourself.

联系我们 - QQ: 99515681 微信:codinghelp
程序辅导网!