diff --git a/image_tagger.c b/image_tagger.c
new file mode 100644
index 0000000000000000000000000000000000000000..735ba45a801115c57ff3e9e8ad2ad9d2bbcedc8c
--- /dev/null
+++ b/image_tagger.c
@@ -0,0 +1,573 @@
+/*
+** http-server.c
+*/
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <strings.h>
+#include <sys/select.h>
+#include <sys/sendfile.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+
+// constants
+static char const * const HTTP_200_FORMAT = "HTTP/1.1 200 OK\r\n\
+Content-Type: text/html\r\n\
+Content-Length: %ld\r\n\r\n";
+static char const * const HTTP_400 = "HTTP/1.1 400 Bad Request\r\nContent-Length: 0\r\n\r\n";
+static int const HTTP_400_LENGTH = 47;
+static char const * const HTTP_404 = "HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\n\r\n";
+static int const HTTP_404_LENGTH = 45;
+static int GAMEOVER_STATE = 7;
+static int READY_STATE = 3;
+static int ENDGAME_STATE = 6;
+static int MAX_LENGTH = 20;
+
+// represents the types of method
+typedef enum
+{
+    GET,
+    POST,
+    UNKNOWN
+} METHOD;
+
+// Structure for player
+typedef struct Player {
+  char player_name[20];
+  int player_socket;
+  int count;
+  char keyword_list[20][20];
+  int player_status;
+}Player;
+
+Player* current_player(int sockfd,Player *player1, Player *player2);
+
+static bool handle_http_request(int sockfd,Player *player1, Player *player2)
+{
+    // try to read the request
+    char buff[2049];
+    char keyword[20];
+    char* starting_keyword;
+    char* ending_keyword;
+    bool end = false;
+    int n = read(sockfd, buff, 2049);
+    if (n <= 0)
+    {
+        if (n < 0)
+            perror("read");
+        else
+            printf("socket %d close the connection\n", sockfd);
+        return false;
+    }
+
+    // terminate the string
+    buff[n] = 0;
+
+    char * curr = buff;
+
+    // parse the method
+    METHOD method = UNKNOWN;
+    if (strncmp(curr, "GET ", 4) == 0)
+    {
+        curr += 4;
+        method = GET;
+    }
+    else if (strncmp(curr, "POST ", 5) == 0)
+    {
+        curr += 5;
+        method = POST;
+    }
+    else if (write(sockfd, HTTP_400, HTTP_400_LENGTH) < 0)
+    {
+        perror("write");
+        return false;
+    }
+
+    // sanitise the URI
+    while (*curr == '.' || *curr == '/')
+        ++curr;
+    // if player quit or the other player quited
+    if((strstr(buff,"quit=Quit"))||player1->player_status == GAMEOVER_STATE ||player2->player_status == GAMEOVER_STATE){
+      // send quit page
+      struct stat st;
+      stat("7_gameover.html", &st);
+      n = sprintf(buff, HTTP_200_FORMAT, st.st_size);
+      // send the header first
+      if (write(sockfd, buff, n) < 0)
+      {
+          perror("write");
+          return false;
+      }
+      // send the file
+      int filefd = open("7_gameover.html", O_RDONLY);
+      do
+      {
+          n = sendfile(sockfd, filefd, NULL, 2048);
+      }
+      while (n > 0);
+      if (n < 0)
+      {
+          perror("sendfile");
+          close(filefd);
+          return false;
+      }
+      close(filefd);
+      // change player status to 7(quit state)
+      if(player1->player_socket==sockfd){
+        player1->player_status=GAMEOVER_STATE;
+      }else if(player2->player_socket == sockfd){
+        player2->player_status=GAMEOVER_STATE;
+      }
+
+      // reset player struct (reset game)
+      if(player1->player_status == GAMEOVER_STATE &&player2->player_status ==GAMEOVER_STATE){
+        player1->player_status = 0;
+        strcpy(player1->player_name,"none");
+        player1->player_socket = 0;
+        player1->count = 0;
+        player2->player_status = 0;
+        strcpy(player2->player_name,"none");
+        player2->player_socket = 0;
+        player2->count = 0;
+      }
+      return false;
+    }
+    // if player guessing
+    else if(strstr(buff,"&guess=Guess")){
+      // if both player are ready
+      if(player1->player_status == READY_STATE && player2->player_status == READY_STATE){
+        // reset keyword
+        memset(keyword, 0, sizeof(char)*20);
+        // find length of keyword and  copy keyword using strncpy
+        starting_keyword = strstr(buff,"keyword=");
+        ending_keyword = strstr(starting_keyword, "&guess=Guess");
+        int keyword_length = ending_keyword-starting_keyword-8;
+        strncpy(keyword,strstr(buff,"keyword=")+8,keyword_length);
+
+        // find current player
+        if(player1->player_socket==sockfd){
+          // place keyword into player keyword_list
+          strcpy(player1->keyword_list[player1->count],keyword);
+          player1->count++;
+          // check if keyword appeared in other player keyword_list
+          for(int k=0; k<=player2->count;k++){
+            // if keyword appeared in other player keyword_list change both player state to victory state(6)
+            if(strcmp(keyword,player2->keyword_list[k])==0){
+                player1->player_status = ENDGAME_STATE;
+                player2->player_status = ENDGAME_STATE;
+            }
+          }
+        }
+        else if(player2->player_socket == sockfd){
+          // place keyword into player keyword_list
+          strcpy(player2->keyword_list[player2->count],keyword);
+          player2->count++;
+          // check if keyword appeared in other player keyword_list
+          for(int k=0; k<=player1->count;k++){
+            // if keyword appeared in other player keyword_list change both player state to endgame state(6)
+            if(strcmp(keyword,player1->keyword_list[k])==0){
+                player1->player_status = ENDGAME_STATE;
+                player2->player_status = ENDGAME_STATE;
+            }
+          }
+        }
+        // if either player in endgame state
+        if (player2->player_status == ENDGAME_STATE||player1->player_status == ENDGAME_STATE) {
+          //reset players keyword_list
+          player1->count = 0;
+          player2->count = 0;
+          memset(player1->keyword_list,0,sizeof(player1->keyword_list [0][0])* 20* 20);
+          memset(player2->keyword_list,0,sizeof(player2->keyword_list [0][0])* 20 * 20);
+
+          // send endgame page
+          struct stat st;
+          stat("6_endgame.html", &st);
+          n = sprintf(buff, HTTP_200_FORMAT, st.st_size);
+          // send the header first
+          if (write(sockfd, buff, n) < 0)
+          {
+              perror("write");
+              return false;
+          }
+          // send the file
+          int filefd = open("6_endgame.html", O_RDONLY);
+          do
+          {
+              n = sendfile(sockfd, filefd, NULL, 2048);
+          }
+          while (n > 0);
+          if (n < 0)
+          {
+              perror("sendfile");
+              close(filefd);
+              return false;
+          }
+          close(filefd);
+            // if player keyword not match(not victory state)
+          }else{
+
+            // send accepted file
+            struct stat st;
+            stat("4_accepted.html", &st);
+            char all_keyword[500];
+            long current_length=0;
+
+            // copy keywords in keyword_list to all_keyword string
+            // calculate the length of extra characters
+            for(int j=0;j<current_player(sockfd,player1,player2)->count;j++){
+              strcpy(all_keyword+current_length,current_player(sockfd,player1,player2)->keyword_list[j]);
+              current_length += strlen(current_player(sockfd,player1,player2)->keyword_list[j]);
+              strcpy(all_keyword+current_length,",");
+              current_length += 1;
+              strcpy(all_keyword+current_length," ");
+              current_length += 1;
+            }
+
+            // add size
+            long size = st.st_size + current_length;
+            n = sprintf(buff, HTTP_200_FORMAT, size);
+            // send the header first
+            if (write(sockfd, buff, n) < 0)
+            {
+                perror("write");
+                return false;
+            }
+            // read the content of the HTML file
+            int filefd = open("4_accepted.html", O_RDONLY);
+            n = read(filefd, buff, 2048);
+            if (n < 0)
+            {
+                perror("read");
+                close(filefd);
+                return false;
+            }
+            close(filefd);
+            // print all_keyword
+            // replace behind with body and html
+            strncpy(strstr(buff,"</body>"), all_keyword,current_length);
+            strncpy(strstr(buff,all_keyword)+current_length,"</body>\n</html>",13);
+
+            if (write(sockfd, buff, size) < 0)
+            {
+                perror("write");
+                return false;
+            }
+          }
+          // check if player in victory state (for player after the other player win)
+        }else if(current_player(sockfd,player1,player2)->player_status == ENDGAME_STATE){
+          // send endgame page
+          struct stat st;
+          stat("6_endgame.html", &st);
+          n = sprintf(buff, HTTP_200_FORMAT, st.st_size);
+          // send the header first
+          if (write(sockfd, buff, n) < 0)
+          {
+              perror("write");
+              return false;
+          }
+          // send the file
+          int filefd = open("6_endgame.html", O_RDONLY);
+          do
+          {
+              n = sendfile(sockfd, filefd, NULL, 2048);
+          }
+          while (n > 0);
+          if (n < 0)
+          {
+              perror("sendfile");
+              close(filefd);
+              return false;
+          }
+          close(filefd);
+          // if other player not ready not in ready state(3)
+        }else{
+          // send discarded page
+        struct stat st;
+        stat("5_discarded.html", &st);
+        n = sprintf(buff, HTTP_200_FORMAT, st.st_size);
+        // send the header first
+        if (write(sockfd, buff, n) < 0)
+        {
+            perror("write");
+            return false;
+        }
+        // send the file
+        int filefd = open("5_discarded.html", O_RDONLY);
+        do
+        {
+            n = sendfile(sockfd, filefd, NULL, 2048);
+        }
+        while (n > 0);
+        if (n < 0)
+        {
+            perror("sendfile");
+            close(filefd);
+            return false;
+        }
+      }
+    }
+    // request intro page
+    else if (*curr == ' '){
+      if (method == GET)
+        {
+            //send intro page
+            struct stat st;
+            stat("1_intro.html", &st);
+            n = sprintf(buff, HTTP_200_FORMAT, st.st_size);
+            // send the header first
+            if (write(sockfd, buff, n) < 0)
+            {
+                perror("write");
+                return false;
+            }
+            // send the file
+            int filefd = open("1_intro.html", O_RDONLY);
+            do
+            {
+                n = sendfile(sockfd, filefd, NULL, 2048);
+            }
+            while (n > 0);
+            if (n < 0)
+            {
+                perror("sendfile");
+                close(filefd);
+                return false;
+            }
+            close(filefd);
+        }
+        else if (method == POST)
+        {
+
+            // locate username
+            char * username = strstr(buff, "user=") + 5;
+            char username_copy[20];
+            // ensure username is empty
+            memset(username_copy, 0, sizeof(char)*MAX_LENGTH);
+            // copy username
+            strcpy(username_copy,username);
+            int username_length = strlen(username);
+            long added_length = username_length;
+
+            //set player name and socket
+            if(!strcmp(player1->player_name, "none")){
+              strcpy(player1->player_name,username);
+              player1->player_socket= sockfd;
+              player1->player_status = 0;
+            }else if(!strcmp(player2->player_name, "none")){
+              strcpy(player2->player_name,username);
+              player2->player_socket = sockfd;
+              player2->player_status = 0;
+            }
+            // get the size of the file
+            struct stat st;
+            stat("2_start.html", &st);
+            // increase file size to accommodate the username
+            long size = st.st_size + added_length;
+            n = sprintf(buff, HTTP_200_FORMAT, size);
+            // send the header first
+            if (write(sockfd, buff, n) < 0)
+            {
+                perror("write");
+                return false;
+            }
+            // read the content of the HTML file
+            int filefd = open("2_start.html", O_RDONLY);
+            n = read(filefd, buff, 2048);
+            if (n < 0)
+            {
+                perror("read");
+                close(filefd);
+                return false;
+            }
+            close(filefd);
+            // move the trailing part backward
+            // copy the username
+            strncpy(strstr(buff,"</body>"), username_copy, username_length);
+            strncpy(strstr(buff,username_copy)+username_length,"</body>\n</html>",13);
+
+
+            if (write(sockfd, buff, size) < 0)
+            {
+                perror("write");
+                return false;
+            }
+        }
+        // request for start (start button pressed)
+      }else if(strstr(buff,"?start=Start")){
+        if (method == GET)
+        {
+          // change player state to read state (3)
+          if(player1->player_socket==sockfd){
+            player1->player_status= READY_STATE;
+          }else if(player2->player_socket == sockfd){
+            player2->player_status= READY_STATE;
+          }
+            // get the size of the file
+            struct stat st;
+            stat("3_first_turn.html", &st);
+            n = sprintf(buff, HTTP_200_FORMAT, st.st_size);
+            // send the header first
+            if (write(sockfd, buff, n) < 0)
+            {
+                perror("write");
+                return false;
+            }
+            // send the file
+            int filefd = open("3_first_turn.html", O_RDONLY);
+            do
+            {
+                n = sendfile(sockfd, filefd, NULL, 2048);
+            }
+            while (n > 0);
+            if (n < 0)
+            {
+                perror("sendfile");
+                close(filefd);
+                return false;
+            }
+            close(filefd);
+        }else{
+            // never used, just for completeness
+            fprintf(stderr, "no other methods supported");
+          }
+    // send 404
+  }
+  else if (write(sockfd, HTTP_404, HTTP_404_LENGTH) < 0)
+    {
+        perror("write");
+        return false;
+    }
+
+    return true;
+}
+
+int main(int argc, char * argv[])
+{
+    if (argc < 3)
+    {
+        fprintf(stderr, "usage: %s ip port\n", argv[0]);
+        return 0;
+    }
+
+    // create TCP socket which only accept IPv4
+    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
+    if (sockfd < 0)
+    {
+        perror("socket");
+        exit(EXIT_FAILURE);
+    }
+
+    // reuse the socket if possible
+    int const reuse = 1;
+    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int)) < 0)
+    {
+        perror("setsockopt");
+        exit(EXIT_FAILURE);
+    }
+
+    // create and initialise address we will listen on
+    struct sockaddr_in serv_addr;
+    bzero(&serv_addr, sizeof(serv_addr));
+    serv_addr.sin_family = AF_INET;
+    // if ip parameter is not specified
+    serv_addr.sin_addr.s_addr = inet_addr(argv[1]);
+    serv_addr.sin_port = htons(atoi(argv[2]));
+
+    //create 2 players
+    Player player1;
+    strcpy(player1.player_name,"none");
+    player1.count = 0;
+    Player player2;
+    strcpy(player2.player_name,"none");
+    player2.count = 0;
+
+    // bind address to socket
+    if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
+    {
+        perror("bind");
+        exit(EXIT_FAILURE);
+    }
+
+    // listen on the socket
+    listen(sockfd, 5);
+
+    // initialise an active file descriptors set
+    fd_set masterfds;
+    FD_ZERO(&masterfds);
+    FD_SET(sockfd, &masterfds);
+    // record the maximum socket number
+    int maxfd = sockfd;
+
+    while (1)
+    {
+        // monitor file descriptors
+        fd_set readfds = masterfds;
+        if (select(FD_SETSIZE, &readfds, NULL, NULL, NULL) < 0)
+        {
+            perror("select");
+            exit(EXIT_FAILURE);
+        }
+
+        // loop all possible descriptor
+        for (int i = 0; i <= maxfd; ++i)
+            // determine if the current file descriptor is active
+            if (FD_ISSET(i, &readfds))
+            {
+                // create new socket if there is new incoming connection request
+                if (i == sockfd)
+                {
+                    struct sockaddr_in cliaddr;
+                    socklen_t clilen = sizeof(cliaddr);
+                    int newsockfd = accept(sockfd, (struct sockaddr *)&cliaddr, &clilen);
+                    if (newsockfd < 0)
+                        perror("accept");
+                    else
+                    {
+                        // add the socket to the set
+                        FD_SET(newsockfd, &masterfds);
+                        // update the maximum tracker
+                        if (newsockfd > maxfd)
+                            maxfd = newsockfd;
+                        // print out the IP and the socket number
+                        char ip[INET_ADDRSTRLEN];
+                        printf(
+                            "new connection from %s on socket %d\n",
+                            // convert to human readable string
+                            inet_ntop(cliaddr.sin_family, &cliaddr.sin_addr, ip, INET_ADDRSTRLEN),
+                            newsockfd
+                        );
+                    }
+                }
+                // a request is sent from the client
+                else if (!handle_http_request(i,&player1,&player2))
+                {
+                    close(i);
+                    FD_CLR(i, &masterfds);
+                }
+            }
+    }
+
+    return 0;
+}
+
+// find current player
+Player* current_player(int sockfd,Player *player1, Player *player2){
+  Player* playerbot;
+  if(player1->player_socket==sockfd){
+    return player1;
+  }else if(player2->player_socket == sockfd){
+    return player2;
+  }
+  return playerbot;
+}