diff --git a/tps/fonte/Game.c b/tps/fonte/Game.c deleted file mode 100644 index 4abec4d..0000000 --- a/tps/fonte/Game.c +++ /dev/null @@ -1,728 +0,0 @@ -/** - - * @file Game.c - * @author Pedro Lopes - * @version 0.2 - * @date 2022-10-02 - * @copyright Copyright (c) 2022 - -**/ - -// -------------------------------------------------------------------------------- // - -// Includes -#include -#include -#include -#include -#include - -// -------------------------------------------------------------------------------- // - -// Definitions -#define MAX_GAMES 9000 -#define MAX_FIELD_SIZE 250 -#define MAX_STRING_ARRAY_SIZE 100 - -// -------------------------------------------------------------------------------- // - -// Structs -typedef struct { - - int year, - month; -} Date; - -typedef struct { - - char name[MAX_FIELD_SIZE], - owners[MAX_FIELD_SIZE], - website[MAX_FIELD_SIZE], - developers[MAX_FIELD_SIZE], - languages[MAX_STRING_ARRAY_SIZE][30], - genres[MAX_STRING_ARRAY_SIZE][30]; - - Date release_date; - int app_id, age, dlcs, avg_playtime, count_languages, count_genres; - float price, upvotes; - bool windows_os, mac_os, linux_os; - -} Game; - -// -------------------------------------------------------------------------------- // - -// Global variables -Game games[MAX_GAMES]; -int n = 0; - -// -------------------------------------------------------------------------------- // - -// Functions -bool isFim(char* line) { return line[0] == 'F' && line[1] == 'I' && line[2] == 'M'; } - -void substring(char *string, char *string_start, int length) { - - strncpy(string, string_start, length); - string[length] = '\0'; -} - -char *getMonthName(int month) { - - switch(month) { - - case 1: return "Jan"; break; - case 2: return "Feb"; break; - case 3: return "Mar"; break; - case 4: return "Apr"; break; - case 5: return "May"; break; - case 6: return "Jun"; break; - case 7: return "Jul"; break; - case 8: return "Aug"; break; - case 9: return "Sep"; break; - case 10: return "Oct"; break; - case 11: return "Nov"; break; - case 12: return "Dec"; break; - - default: return "N/A"; break; - } -} - -int getMonthNumber(char* month) { - - if(!strcmp(month, "Jan")) return 1; - else if(!strcmp(month, "Feb")) return 2; - else if(!strcmp(month, "Mar")) return 3; - else if(!strcmp(month, "Apr")) return 4; - else if(!strcmp(month, "May")) return 5; - else if(!strcmp(month, "Jun")) return 6; - else if(!strcmp(month, "Jul")) return 7; - else if(!strcmp(month, "Aug")) return 8; - else if(!strcmp(month, "Sep")) return 9; - else if(!strcmp(month, "Oct")) return 10; - else if(!strcmp(month, "Nov")) return 11; - else if(!strcmp(month, "Dec")) return 12; -} - -// -------------------------------------------------------------------------------- // - -// Class game functions -void game_start(Game *game) { - - strcpy(game -> name, ""); - strcpy(game -> owners, ""); - strcpy(game -> website, ""); - strcpy(game -> developers, ""); - - for(int i = 0; i < MAX_STRING_ARRAY_SIZE; i++) { - - strcpy(game -> languages[i], ""); - strcpy(game -> genres[i], ""); - } - - game -> release_date.month = -1; - game -> release_date.year = -1; - game -> app_id = -1; - game -> age = -1; - game -> dlcs = -1; - game -> avg_playtime = -1; - game -> price = -1; - game -> upvotes = -1; - game -> windows_os = false; - game -> mac_os = false; - game -> linux_os = false; - - game -> count_languages = 0; - game -> count_genres = 0; -} - -void game_print(Game *game) { - - int hours = game -> avg_playtime / 60, - minutes = game -> avg_playtime % 60; - - printf("%i %s %s/%04i %s %i %.2f %i [", game -> app_id, game -> name, getMonthName(game -> release_date.month), game -> release_date.year, game -> owners, game -> age, game -> price, game -> dlcs); - - for(int i = 0; i < game -> count_languages; i++) { - - printf("%s%s", game -> languages[i], i < game -> count_languages - 1 ? ", " : ""); - } - - printf("] %s %s %s %s ", game -> website, game -> windows_os ? "true" : "false", game -> mac_os ? "true" : "false", game -> linux_os ? "true" : "false"); - - if(isnan(game -> upvotes)) printf("0.0%% "); - else printf("%.0f%% ", game -> upvotes); - - if(hours > 0) - { - printf("%ih ", hours); - - if(minutes > 0) printf("%im ", minutes); - } - else { - - if(minutes > 0) printf("%im ", minutes); - else printf("null "); - } - - printf("%s [", game -> developers); - - for(int i = 0; i < game -> count_genres; i++) { - - printf("%s%s", game -> genres[i], i < game -> count_genres - 1 ? ", " : ""); - } - - printf("]\n"); -} - -Game game_clone(Game *game) { - - Game cloned; - - strcpy(cloned.name, game -> name); - strcpy(cloned.owners, game -> owners); - strcpy(cloned.website, game -> website); - strcpy(cloned.developers, game -> developers); - - for(int i = 0; i < game -> count_languages; i++) strcpy(cloned.languages[i], game -> languages[i]); - for(int i = 0; i < game -> count_genres; i++) strcpy(cloned.genres[i], game -> genres[i]); - - cloned.release_date.month = game -> release_date.month; - cloned.release_date.year = game -> release_date.year; - cloned.app_id = game -> app_id; - cloned.age = game -> age; - cloned.dlcs = game -> dlcs; - cloned.avg_playtime = game -> avg_playtime; - cloned.price = game -> price; - cloned.upvotes = game -> upvotes; - cloned.windows_os = game -> windows_os; - cloned.mac_os = game -> mac_os; - cloned.linux_os = game -> linux_os; - return cloned; -} - -void game_read(Game *game, char *line) { - - char c_search, sub[MAX_FIELD_SIZE]; - int index = 0, atr_index = 0; - - // ------------------------------------------------------------ // - - // Find "AppID" - while(true) { - - index++; - - if(line[index] == ',') { - - substring(sub, &line[atr_index], index - atr_index); - - game -> app_id = atoi(sub); - - atr_index = ++index; - break; - } - } - - // ------------------------------------------------------------ // - - // Find "Name" - if(line[atr_index] != ',') { - - if(line[atr_index] == '\"') { - - atr_index++; - c_search = '\"'; - } - else c_search = ','; - - while(true) { - - index++; - - if(line[index] == c_search) { - - substring(sub, &line[atr_index], index - atr_index); - strcpy(game -> name, sub); - - if(c_search == ',') index++; - else if(c_search == '\"') index += 2; - - atr_index = index; - break; - } - } - } - else { - - strcpy(game -> name, "null"); - - atr_index = ++index; - } - - // ------------------------------------------------------------ // - - // Find release date - if(line[atr_index] != ',') { - - if(line[atr_index] == '\"') { - - atr_index++; - c_search = '\"'; - } - else c_search = ','; - - while(true) { - - index++; - - if(line[index] == c_search) { - - substring(sub, &line[atr_index], index - atr_index); - - char subDate[10]; - - substring(subDate, &sub[0], 3); - - game -> release_date.month = getMonthNumber(subDate); - - if(c_search == ',') { - - substring(subDate, &sub[4], 4); - - game -> release_date.year = atoi(subDate); - - index++; - } - else if(c_search == '\"') { - - int nmbSpace = 0; - - for(int i = 0; ; i++) { - - if(sub[i] == ' ') nmbSpace++; - - if(nmbSpace == 2) { - - i++; - - substring(subDate, &sub[i], 4); - - game -> release_date.year = atoi(subDate); - break; - } - } - - index += 2; - } - - atr_index = index; - break; - } - } - } - else { - - game -> release_date.month = 0; - game -> release_date.year = 0; - - atr_index = ++index; - } - - // ------------------------------------------------------------ // - - // Find "Owners" - while(true) { - - index++; - - if(line[index] == ',') { - - substring(sub, &line[atr_index], index - atr_index); - strcpy(game -> owners, sub); - - atr_index = ++index; - break; - } - } - - // ------------------------------------------------------------ // - - // Find "Age" - while(true) { - - index++; - - if(line[index] == ',') { - - substring(sub, &line[atr_index], index - atr_index); - - game -> age = atoi(sub); - - atr_index = ++index; - break; - } - } - - // ------------------------------------------------------------ // - - // Find "Price" - while(true) { - - index++; - - if(line[index] == ',') { - - substring(sub, &line[atr_index], index - atr_index); - - game -> price = atof(sub); - - atr_index = ++index; - break; - } - } - - // ------------------------------------------------------------ // - - // Find "DLCs" - while(true) { - - index++; - - if(line[index] == ',') { - - substring(sub, &line[atr_index], index - atr_index); - - game -> dlcs = atoi(sub); - - atr_index = ++index; - break; - } - } - - // ------------------------------------------------------------ // - - // Find "Languages" - while(true) { - - index++; - - if(line[index] == ']') { - - index++; - - if(line[index] == ',') index++; - else if(line[index] == '\"') index += 2; - - atr_index = index; - break; - } - else if(line[index] == '\'') { - - int wordStart = index + 1; - - while(true) { - - index++; - - if(line[index] == '\'') { - - substring(sub, &line[wordStart], index - wordStart); - strcpy(game -> languages[game -> count_languages++], sub); - break; - } - } - } - } - - // ------------------------------------------------------------ // - - // Find "Website" - if(line[atr_index] != ',') { - - if(line[atr_index] == '\"') { - - atr_index++; - c_search = '\"'; - } - else c_search = ','; - - while(true) { - - index++; - - if(line[index] == c_search) { - - substring(sub, &line[atr_index], index - atr_index); - strcpy(game -> website, sub); - - atr_index = ++index; - break; - } - } - } - else { - - strcpy(game -> website, "null"); - - atr_index = ++index; - } - - // ------------------------------------------------------------ // - - // Find "Windows" - while(true) { - - index++; - - if(line[index] == ',') { - - substring(sub, &line[atr_index], index - atr_index); - - if(!strcmp(sub, "True")) game -> windows_os = true; - - atr_index = ++index; - break; - } - } - - // Find "Mac" - while(true) { - - index++; - - if(line[index] == ',') { - - substring(sub, &line[atr_index], index - atr_index); - - if(!strcmp(sub, "True")) game -> mac_os = true; - - atr_index = ++index; - break; - } - } - - // Find "Linux" - while(true) { - - index++; - - if(line[index] == ',') { - - substring(sub, &line[atr_index], index - atr_index); - - if(!strcmp(sub, "True")) game -> linux_os = true; - - atr_index = ++index; - break; - } - } - - // ------------------------------------------------------------ // - - // Find "Upvotes" - int positives, negatives; - - while(true) { - - index++; - - if(line[index] == ',') { - - substring(sub, &line[atr_index], index - atr_index); - - positives = atoi(sub); - atr_index = ++index; - break; - } - } - - while(true) { - - index++; - - if(line[index] == ',') { - - substring(sub, &line[atr_index], index - atr_index); - - negatives = atoi(sub); - atr_index = ++index; - break; - } - } - - game -> upvotes = (float)(positives * 100) / (float)(positives + negatives); - - // ------------------------------------------------------------ // - - // Find "AVG Playtime" - while(true) { - - index++; - - if(line[index] == ',') { - - substring(sub, &line[atr_index], index - atr_index); - - game -> avg_playtime = atoi(sub); - - atr_index = ++index; - break; - } - } - - // ------------------------------------------------------------ // - - // Find "Developers" - if(line[atr_index] != ',') { - - if(line[atr_index] == '\"') { - - atr_index++; - c_search = '\"'; - } - else c_search = ','; - - while(true) { - - index++; - - if(line[index] == c_search) { - - substring(sub, &line[atr_index], index - atr_index); - strcpy(game -> developers, sub); - - atr_index = ++index; - break; - } - } - } - else { - - strcpy(game -> developers, "null"); - - atr_index = ++index; - } - - // ------------------------------------------------------------ // - - // Find "Genres" - if(index < strlen(line) - 1) { - - if(line[index] == ',') atr_index = ++index; - - if(line[atr_index] == '\"') { - - atr_index++; - - while(true) { - - index++; - - if(line[index] == ',') { - - substring(sub, &line[atr_index], index - atr_index); - strcpy(game -> genres[game -> count_genres++], sub); - - atr_index = ++index; - } - else if(line[index] == '\"') { - - substring(sub, &line[atr_index], strlen(line) - 1 - atr_index); - - if(sub[strlen(sub) - 1] == '\"') sub[strlen(sub) - 1] = '\0'; - else if(sub[strlen(sub) - 2] == '\"') sub[strlen(sub) - 2] = '\0'; - - strcpy(game -> genres[game -> count_genres++], sub); - break; - } - } - } - else { - - substring(sub, &line[atr_index], strlen(line) - 2 - atr_index); - - strcpy(game -> genres[game -> count_genres++], sub); - } - } -} - -// -------------------------------------------------------------------------------- // - -// Functions - List -void list_insert(Game x) { - - if(n >= MAX_GAMES) { - - printf("Insert error: MAX_GAMES reached"); - exit(1); - } - - games[n++] = x; -} - -// ---------------------------------------------------------------------------------------------------------- // - -int main() { - - // ------------------------------------------------------------ // - - // First part - Read all games - - // Open CSV file - FILE *fp; - char *line = NULL; - size_t len = 0; - size_t read; - - fp = fopen("/tmp/games.csv", "r"); - - if(fp == NULL) exit(EXIT_FAILURE); - - // ------------------------- // - - // Fill games list - while((read = getline(&line, &len, fp)) != -1) { - - Game game; - - game_start(&game); - game_read(&game, line); - list_insert(game); - } - - // ------------------------- // - - fclose(fp); - - if(line) free(line); - - // ------------------------------------------------------------ // - - // Second part - Print .in games - - char in[15]; - scanf(" %[^\n]s", in); - - while(true) { - - if(isFim(in)) break; - else { - - int app_id = atoi(in); - - for(int i = 0; i < n; i++) { - - if(games[i].app_id == app_id) { - - game_print(&games[i]); - break; - } - } - - // ------------------------- // - - scanf(" %[^\n]s", in); - } - } - return EXIT_SUCCESS; -} \ No newline at end of file diff --git a/tps/fonte/Game.java b/tps/fonte/Game.java deleted file mode 100644 index be149f5..0000000 --- a/tps/fonte/Game.java +++ /dev/null @@ -1,577 +0,0 @@ -/** - - * @file Game.java - * @author Pedro Lopes - * @version 0.2 - * @date 2022-10-02 - * @copyright Copyright (c) 2022 - -**/ - -// ----------------------------------------------------------------------------------------------------------------- // - -import java.io.BufferedReader; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStreamReader; -import java.text.DecimalFormat; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Date; -import java.util.Locale; -import java.util.Scanner; - -// ----------------------------------------------------------------------------------------------------------------- // - -class Game { - - static SimpleDateFormat default_dateFormat = new SimpleDateFormat("MMM/yyyy", Locale.ENGLISH); - - private String name, owners, website, developers; - private ArrayList languages, genres; - private Date release_date; - private int app_id, age, dlcs, avg_playtime; - private float price, upvotes; - private boolean windows, mac, linux; - - public Game() { - - this.name = this.owners = this.website = this.developers = null; - this.languages = new ArrayList(); - this.genres = new ArrayList(); - this.release_date = null; - this.app_id = this.age = this.dlcs = this.avg_playtime = -1; - this.price = this.upvotes = -1; - this.windows = this.mac = this.linux = false; - } - - public Game(String name, String owners, String website, String developers, ArrayList languages, ArrayList genres, Date release_date, int app_id, int age, int dlcs, int upvotes, int avg_playtime, float price, boolean windows, boolean mac, boolean linux) { - - this.name = name; - this.owners = owners; - this.website = website; - this.developers = developers; - this.languages = languages; - this.genres = genres; - this.release_date = release_date; - this.app_id = app_id; - this.age = age; - this.dlcs = dlcs; - this.upvotes = upvotes; - this.avg_playtime = avg_playtime; - this.price = price; - this.windows = windows; - this.mac = mac; - this.linux = linux; - } - - public void setName(String name) { this.name = name; } - public void setOwners(String owners) { this.owners = owners; } - public void setWebsite(String website) { this.website = website; } - public void setDevelopers(String developers) { this.developers = developers; } - public void setLanguages(ArrayList languages) { this.languages = languages; } - public void setGenres(ArrayList genres) { this.genres = genres; } - public void setReleaseDate(Date release_date) { this.release_date = release_date; } - public void setAppId(int app_id) { this.app_id = app_id; } - public void setAge(int age) { this.age = age; } - public void setDlcs(int dlcs) { this.dlcs = dlcs; } - public void setAvgPlaytime(int avg_playtime) { this.avg_playtime = avg_playtime; } - public void setPrice(float price) { this.price = price; } - public void setUpvotes(float upvotes) { this.upvotes = upvotes; } - public void setWindows(boolean windows) { this.windows = windows; } - public void setMac(boolean mac) { this.mac = mac; } - public void setLinux(boolean linux) { this.linux = linux; } - - public String getName() { return this.name; } - public String getOwners() { return this.owners; } - public String getWebsite() { return this.website; } - public String getDevelopers() { return this.developers; } - public ArrayList getLanguages() { return this.languages; } - public ArrayList getGenres() { return this.genres; } - public Date getReleaseDate() { return this.release_date; } - public int getAppId() { return this.app_id; } - public int getAge() { return this.age; } - public int getDlcs() { return this.dlcs; } - public int getAvgPlaytime() { return this.avg_playtime; } - public float getPrice() { return this.price; } - public float getUpvotes() { return this.upvotes; } - public boolean getWindows() { return this.windows; } - public boolean getMac() { return this.mac; } - public boolean getLinux() { return this.linux; } - - public Game clone() { - - Game cloned = new Game(); - - cloned.name = this.name; - cloned.owners = this.owners; - cloned.website = this.website; - cloned.developers = this.developers; - cloned.languages = this.languages; - cloned.genres = this.genres; - cloned.release_date = this.release_date; - cloned.app_id = this.app_id; - cloned.age = this.age; - cloned.dlcs = this.dlcs; - cloned.avg_playtime = this.avg_playtime; - cloned.price = this.price; - cloned.upvotes = this.upvotes; - cloned.windows = this.windows; - cloned.mac = this.mac; - cloned.linux = this.linux; - - return cloned; - } - - public void read(String line) { - - char c_search; - int index = 0, atr_index = 0; - - // ---------------------------------- // - - // Find "AppID" - while(true) { - - index++; - - if(line.charAt(index) == ',') { - - this.app_id = Integer.parseInt(line.substring(atr_index, index)); - - atr_index = ++index; - break; - } - } - - // ---------------------------------- // - - // Find "Name" - if(line.charAt(atr_index) != ',') { - - if(line.charAt(atr_index) == '\"') { - - atr_index++; - c_search = '\"'; - } - else c_search = ','; - - while(true) { - - index++; - - if(line.charAt(index) == c_search) { - - this.name = line.substring(atr_index, index); - - if(c_search == ',') index++; - else if(c_search == '\"') index += 2; - - atr_index = index; - break; - } - } - } - else atr_index = ++index; - - // ---------------------------------- // - - // Find release date - if(line.charAt(atr_index) != ',') { - - SimpleDateFormat df; - - if(line.charAt(atr_index) == '\"') { - - df = new SimpleDateFormat("MMM dd, yyyy", Locale.ENGLISH); - - atr_index++; - c_search = '\"'; - } - else { - - df = new SimpleDateFormat("MMM yyyy", Locale.ENGLISH); - - c_search = ','; - } - - while(true) { - - index++; - - if(line.charAt(index) == c_search) { - - try { this.release_date = df.parse(line.substring(atr_index, index)); } - catch (java.text.ParseException e) { e.printStackTrace(); } - - if(c_search == ',') index++; - else if(c_search == '\"') index += 2; - - atr_index = index; - break; - } - } - } - else atr_index = ++index; - - // ---------------------------------- // - - // Find "Owners" - while(true) { - - index++; - - if(line.charAt(index) == ',') { - - this.owners = line.substring(atr_index, index); - - atr_index = ++index; - break; - } - } - - // ---------------------------------- // - - // Find "Age" - while(true) { - - index++; - - if(line.charAt(index) == ',') { - - this.age = Integer.parseInt(line.substring(atr_index, index)); - - atr_index = ++index; - break; - } - } - - // ---------------------------------- // - - // Find "Price" - while(true) { - - index++; - - if(line.charAt(index) == ',') { - - this.price = Float.parseFloat(line.substring(atr_index, index)); - - atr_index = ++index; - break; - } - } - - // ---------------------------------- // - - // Find "DLCs" - while(true) { - - index++; - - if(line.charAt(index) == ',') { - - this.dlcs = Integer.parseInt(line.substring(atr_index, index)); - - atr_index = ++index; - break; - } - } - - // ---------------------------------- // - - // Find "Languages" - while(true) { - - index++; - - if(line.charAt(index) == ']') { - - index++; - - if(line.charAt(index) == ',') index++; - else if(line.charAt(index) == '\"') index += 2; - - atr_index = index; - break; - } - else if(line.charAt(index) == '\'') { - - int wordStart = index + 1; - - while(true) { - - index++; - - if(line.charAt(index) == '\'') { - - this.languages.add(line.substring(wordStart, index)); - break; - } - } - } - } - - // ---------------------------------- // - - // Find "Website" - if(line.charAt(atr_index) != ',') { - - if(line.charAt(atr_index) == '\"') { - - atr_index++; - c_search = '\"'; - } - else c_search = ','; - - while(true) { - - index++; - - if(line.charAt(index) == c_search) { - - this.website = line.substring(atr_index, index); - - atr_index = ++index; - break; - } - } - } - else atr_index = ++index; - - // ---------------------------------- // - - // Find "Windows" - while(true) { - - index++; - - if(line.charAt(index) == ',') { - - this.windows = Boolean.parseBoolean(line.substring(atr_index, index)); - - atr_index = ++index; - break; - } - } - - // Find "Mac" - while(true) { - - index++; - - if(line.charAt(index) == ',') { - - this.mac = Boolean.parseBoolean(line.substring(atr_index, index)); - - atr_index = ++index; - break; - } - } - - // Find "Linux" - while(true) { - - index++; - - if(line.charAt(index) == ',') { - - this.linux = Boolean.parseBoolean(line.substring(atr_index, index)); - - atr_index = ++index; - break; - } - } - - // ---------------------------------- // - - // Find "Upvotes" - int positives, negatives; - - while(true) { - - index++; - - if(line.charAt(index) == ',') { - - positives = Integer.parseInt(line.substring(atr_index, index)); - - atr_index = ++index; - break; - } - } - - while(true) { - - index++; - - if(line.charAt(index) == ',') { - - negatives = Integer.parseInt(line.substring(atr_index, index)); - - atr_index = ++index; - break; - } - } - - this.upvotes = (float)(positives * 100) / (float)(positives + negatives); - - // ---------------------------------- // - - // Find "AVG Playtime" - while(true) { - - index++; - - if(line.charAt(index) == ',') { - - this.avg_playtime = Integer.parseInt(line.substring(atr_index, index)); - - atr_index = ++index; - break; - } - } - - // ---------------------------------- // - - // Find "Developers" - if(line.charAt(atr_index) != ',') { - - if(line.charAt(atr_index) == '\"') { - - atr_index++; - c_search = '\"'; - } - else c_search = ','; - - while(true) { - - index++; - - if(line.charAt(index) == c_search) { - - this.developers = line.substring(atr_index, index); - - atr_index = ++index; - break; - } - } - } - else atr_index = ++index; - - // ---------------------------------- // - - // Find "Genres" - if(index < line.length() - 1) { - - if(line.charAt(index) == ',') atr_index = ++index; - if(line.charAt(atr_index) == '\"') { - - atr_index++; - - while(true) { - - index++; - - if(line.charAt(index) == ',') { - - this.genres.add(line.substring(atr_index, index)); - - atr_index = ++index; - } - else if(line.charAt(index) == '\"') { - - this.genres.add(line.substring(atr_index, line.length() - 1)); - break; - } - } - } - else this.genres.add(line.substring(atr_index, line.length())); - } - - // -------------------------------------------------------------------------------- // - } - - public void print() { - - String avg_pt = null; - - if(this.avg_playtime == 0) avg_pt = "null "; - else if(this.avg_playtime < 60) avg_pt = this.avg_playtime + "m "; - else { - - if(this.avg_playtime % 60 == 0) avg_pt = this.avg_playtime / 60 + "h "; - else avg_pt = (this.avg_playtime / 60) + "h " + (this.avg_playtime % 60) + "m "; - } - - DecimalFormat df = new DecimalFormat("##"); - - System.out.println(this.app_id + " " + this.name + " " + default_dateFormat.format(this.release_date) + " " + this.owners + " " + this.age + " " + String.format(Locale.ENGLISH, "%.2f", this.price) + " " + this.dlcs + " " + this.languages + " " + this.website + " " + this.windows + " " + this.mac + " " + this.linux + " " + (Float.isNaN(this.upvotes) ? "0% " : df.format(this.upvotes) + "% ") + avg_pt + this.developers + " " + this.genres); - } - - // -------------------------------------------------------------------------------------- // - - public static boolean isFim(String line) { return line.compareTo("FIM") == 0; } - - // -------------------------------------------------------------------------------------- // - - public static void main(String[] args) throws Exception { - - ArrayList games = new ArrayList(); - - // ------------------------------------------------------------------------------ // - - try { - - // Read CSV file - String basefile = "/tmp/games.csv"; - - FileInputStream fstream = new FileInputStream(basefile); - BufferedReader br = new BufferedReader(new InputStreamReader(fstream)); - - // ------------------------------------ // - - // Explode CSV file reading games - String line; - - while((line = br.readLine()) != null) { - - Game game = new Game(); - - game.read(line); - games.add(game); - } - - // Close CSV file - fstream.close(); - } - catch(IOException e) { e.printStackTrace(); } - - // ---------------------------------------------------------------------------------------------- // - - // Read .in file - Scanner scr = new Scanner(System.in); - String line = scr.nextLine(); - - while(true) { - - if(isFim(line)) break; - - int app_id = Integer.parseInt(line); - - // Search game with .in id - for(Game game : games) { - - if(game.getAppId() == app_id) game.print(); - } - - line = scr.nextLine(); - } - - // ---------------------------------------------------------------------------------------------- // - - scr.close(); - } - - // ---------------------------------------------------------------------------------------------- // -} \ No newline at end of file diff --git a/tps/fonte/game01.c b/tps/gabaritos/tp02/TP02Q02 - Registro em C/Game_alt.c similarity index 96% rename from tps/fonte/game01.c rename to tps/gabaritos/tp02/TP02Q02 - Registro em C/Game_alt.c index 39becc0..5c96434 100644 --- a/tps/fonte/game01.c +++ b/tps/gabaritos/tp02/TP02Q02 - Registro em C/Game_alt.c @@ -1,721 +1,721 @@ -// ------------------------------------------------------------------------------ // -// Author: Vinicius Gabriel dos Santos - https://github.com/ravixr -// Sep/2022 -// -// DESCRIPTION: -// In addition to the game data structure and -// the solution to the second question of the assignment, -// this file contains a basic implementation of a string type -// which was extensively used in the game data structure -// as well as in the solution algorithm. -// ------------------------------------------------------------------------------ // - -#include -#include -#include -#include -#include -#include - -#define GAME_FIELDS_SIZE 17 -#define MAX_FIELD_SIZE 500 -#define MAX_LINE_SIZE 1000 -#define MAX_GAMES 100 - -// If on windows remove first slash -> 'tmp/games.csv' -#define FILE_PATH "/tmp/games.csv" - -// Assert macro to print custom message when assertion fails -#define assert__(x) for (; !(x); assert(x)) - -// --------------------------- my_very_basic_string.h --------------------------- // - -typedef struct string_t string; - -typedef struct strarr_t strarr; - -struct string_t { - char *buf; - char (*char_at)(const string *, size_t); - int (*compare)(const string *, const string *); - void (*delete)(string *); - size_t (*find)(const string *, const char *); - size_t (*length)(const string *); - void (*replace)(string *, const char *, const char *); - strarr (*split)(const string *, const char *); - string (*substr)(const string *, size_t, size_t); - float (*to_float)(const string *); - int (*to_int)(const string *); - void (*trim)(string *); -}; - -struct strarr_t { - string *src; - size_t length; - void (*delete)(strarr *); -}; - -// string functions declaration -char str_char_at(const string *, size_t); -int str_compare(const string *, const string *); -void str_delete(string *); -size_t str_find(const string *, const char *); -size_t str_length(const string *); -void str_replace(string *, const char*, const char *); -string new_string(char *); -strarr str_split(const string *, const char *); -string str_substr(const string *, size_t, size_t); -float str_to_float(const string *); -int str_to_int(const string *); -void str_trim(string *); - -// strarr functions declaration -strarr new_strarr(string *, size_t); -void strarr_delete(strarr *); - -// --------------------------- my_very_basic_string.c --------------------------- // - -// Fuction to initialize string type -string new_string(char *src) -{ - string str; - if (src != NULL) { - if (strcmp(src, "") != 0) { - str.buf = malloc(sizeof(char) * (strlen(src) + 1)); - strcpy(str.buf, src); - } else { - str.buf = malloc(sizeof(char) * 5); - strcpy(str.buf, "null"); - } - } else str.buf = NULL; - str.char_at = &str_char_at; - str.compare = &str_compare; - str.delete = &str_delete; - str.find = &str_find; - str.replace = &str_replace; - str.length = &str_length; - str.split = &str_split; - str.substr = &str_substr; - str.to_float = &str_to_float; - str.to_int = &str_to_int; - str.trim = &str_trim; - return str; -} - -// Fuction to deallocate string type -void str_delete(string *self) -{ - free(self->buf); -} - -// Returns character present in the specified index of the string -// e.g: "foo" idx: 0 -> 'f' -char str_char_at(const string *self, size_t idx) -{ - // Macro to assert index is in bounds - assert__(idx < strlen(self->buf)) { - fprintf(stderr, "String index %ld is out of bounds\n", idx); - } - return self->buf[idx]; -} - -// Allows comparison between string type variables using strcmp in the raw data -int str_compare(const string *self, const string *another_str) -{ - return strcmp(self->buf, another_str->buf); -} - -// Searches the string for the first occurrence of the pattern specified. -// Returns the index position of the first character of the pattern -// or the length of the string if the pattern is not found -// e.g.: "foo bar", "bar" -> 4 | "foo bar", "baz" -> 7 -size_t str_find(const string *self, const char *lkf) -{ - size_t i = 0, - self_len = strlen(self->buf), - lkf_len = strlen(lkf); - - // Macro to assert pattern string is valid - assert__((strcmp(lkf, "") != 0) && (lkf_len <= self_len)) { - fprintf(stderr, "Pattern '%s' is invalid for the specified string\n", lkf); - } - - // Find algorithm - for (size_t j = 0; i < self_len; i++) { - if (self->buf[i] == lkf[j]) { - j++; - } else { - i-= j; j = 0; - } - if (j == lkf_len) { - i -= (j - 1); - break; - } - } - return i; -} - -// Returns the length of the string using strlen in the raw data -size_t str_length(const string *self) -{ - return strlen(self->buf); -} - -// Replaces all occurrences of specified pattern in the string with the new specified pattern -// if the string doesn't contains the pattern to be replaced the string is not changed -// e.g.: "foobar", "bar", "" -> "foo" | "foobar", "baz", "" -> "foobar" -void str_replace(string *self, const char *old_val, const char *new_val) -{ - // Saving useful lengths in variables - size_t old_len = strlen(self->buf), - nv_len = strlen(new_val), - ov_len = strlen(old_val); - - // Macro to assert given pattern is valid - assert__((strcmp(old_val, "") != 0) && (old_len >= ov_len)) { - fprintf(stderr, "Invalid pattern for argument 'old_val'\n"); - } - - // Calculating the max possible amount of patterns in the string - // then allocating an array to store their indexes - size_t idx_sz = (old_len / ov_len), - *idx = malloc(sizeof(size_t) * (idx_sz + 1)), - idx_i = 0, last_idx = 0, skip = 0; - - // Getting all starting indexes of where the pattern is found - string aux = new_string(NULL); - aux.buf = (self->buf); - while ((idx_i <= idx_sz) && (last_idx < old_len)) { - idx[idx_i] = str_find(&aux, old_val) + skip; - skip = idx[idx_i++] + ov_len; - aux.buf = (self->buf + skip); - last_idx = idx[idx_i - 1]; - } - - // Checking if the pattern is actually on the string - if (idx[0] != old_len) { - // Assuring 'idx_i' will not be 0 - idx_i > 1 ? (idx_i -= 1) : (idx_i = 1); - - // Calculating new length of the string then allocating - size_t new_len = (old_len + ((nv_len - ov_len) * idx_i)); - char *tmp = malloc(sizeof(char) * (new_len + 1)); - - // Main Replacement algorithm: copies old string char by char, - // when the index is equal to the range of the next found pattern - // starts copying the new pattern char by chat into the string - if (nv_len == 0) nv_len = 1; - for (size_t i = 0, j = 0, k = 0, l = 0; i < new_len;) { - if (k >= idx[l] && k < (idx[l] + ov_len)) { - if (strcmp(new_val, "") != 0) - tmp[i++] = new_val[j]; - j++; - if (j == nv_len) { - k += ov_len; - j = 0; l++; - } - } else tmp[i++] = self->buf[k++]; - } - tmp[new_len] = '\0'; // Adding null terminator in the new string - free(self->buf); // freeing old string - self->buf = tmp; // Pointing to new string - } - free(idx); // freeing array of indexes of the old string -} - -// Uses strtok to split the raw char array -// then build new strings with the splitted char arrays -// and bundle all of them in the strarr type -// e.g.: 'foo,bar' ',' -> ['foo', 'bar'] -strarr str_split(const string *self, const char *delim) -{ - char *str = malloc(sizeof(char) * (strlen(self->buf) + 1)); - strcpy(str, self->buf); - char *token = strtok(str, delim); - size_t count = 0; - strarr str_arr = new_strarr(NULL, 0); - string *tmp; - while(token != NULL) { - tmp = realloc(str_arr.src, sizeof(string) * (count + 1)); - if (tmp != NULL) { - tmp[count++] = new_string(token); - str_arr.src = tmp; - } - token = strtok(NULL, delim); - } - free(str); - free(token); - str_arr.length = count; - return str_arr; -} - -// Returns a new string that is a copy of the portion of the original string -// that starts at character position 'pos' and spans 'len' characters -// e.g.: "foo bar" pos: 4, len: 3 -> "bar" -string str_substr(const string *self, size_t pos, size_t len) -{ - // Macro to assert pos and len are in bounds - assert__((pos + len) <= strlen(self->buf)) { - fprintf(stderr, "String index %ld is out of bounds for '%s'\n", (pos + len), self->buf); - } - string substr = new_string(NULL); - substr.buf = malloc(sizeof(char) * (len + 1)); - for (size_t i = 0, j = 0; i < len; i++, j++) { - substr.buf[j] = self->buf[pos + i]; - } - substr.buf[len] = '\0'; - return substr; -} - -// Uses atof to convert string to float -float str_to_float(const string *self) -{ - return atof(self->buf); -} - -// Uses atoi to convert string to int -int str_to_int(const string *self) -{ - return atoi(self->buf); -} - -// Removes all spaces only from the beginning and ending of the string -// e.g.: " foo " -> "foo" | "foo bar" -> "foo bar" -void str_trim(string *self) -{ - size_t i = 0, j = (strlen(self->buf) - 1); - while (self->buf[i] == ' ' || self->buf[j] == ' ') { - if (self->buf[i] == ' ') i++; - if (self->buf[j] == ' ') j--; - } - string tmp = self->substr(self, i, (j - i) + 1); - free(self->buf); - self->buf = tmp.buf; -} - -// Fuction to initialize string array type -strarr new_strarr(string *src, size_t src_length) -{ - strarr str_arr; - if (src != NULL) - str_arr.src = src; - else { - src_length = (src_length > 0 ? src_length : 1); - str_arr.src = malloc(sizeof(string *)); - } - str_arr.length = src_length; - str_arr.delete = &strarr_delete; - return str_arr; -} - -// Fuction to deallocate string array type -void strarr_delete(strarr *self) -{ - for (size_t i = 0; i < self->length; i++) { - free(self->src[i].buf); - } - free(self->src); -} - -// ----------------------------------- game.h ----------------------------------- // - -typedef struct date_t date; - -struct date_t { - uint8_t day; - char month[4]; - unsigned year; - size_t value; -}; - -date new_date(string *); -unsigned strmon_to_int(const char *); - -typedef struct game_t game; - -struct game_t { - unsigned app_id; - string name; - date release_date; - string owners; - unsigned age; - float price; - unsigned dlcs; - strarr languages; - string website; - bool windows; - bool mac; - bool linux_; - float upvotes; - unsigned avg_pt; - string developers; - strarr genres; -}; - -game empty_game(void); -game new_game(string *); -game str_to_game(string *); -game chararr_to_game(char *); -void game_print(game *); -void game_read_csv(game *, size_t *, size_t); -char *avgpt_to_chararr (unsigned); - -// ----------------------------------- game.c ----------------------------------- // - -// Converts month abbreviated char array in format 'Mmm' -// to unsigned int, if unsuccessful returns 0 -// e.g.: "Aug" -> 8 | "aug" -> 0 -unsigned strmon_to_int(const char *mon) -{ - unsigned i = 0; - if (strcmp(mon, "Jan") == 0) { i = 1; } - else if (strcmp(mon, "Feb") == 0) { i = 2; } - else if (strcmp(mon, "Mar") == 0) { i = 3; } - else if (strcmp(mon, "Apr") == 0) { i = 4; } - else if (strcmp(mon, "May") == 0) { i = 5; } - else if (strcmp(mon, "Jun") == 0) { i = 6; } - else if (strcmp(mon, "Jul") == 0) { i = 7; } - else if (strcmp(mon, "Aug") == 0) { i = 8; } - else if (strcmp(mon, "Sep") == 0) { i = 9; } - else if (strcmp(mon, "Oct") == 0) { i = 10; } - else if (strcmp(mon, "Nov") == 0) { i = 11; } - else if (strcmp(mon, "Dec") == 0) { i = 12; } - return i; -} - -// Fuction to initialize date type -// Assumes that argument is a string -// in the format 'mmm dd, yyyy' or 'mmm yyyy' -// or NULL for empty initialization -date new_date(string *fmt) -{ - date d; - if (fmt != NULL) { - uint8_t i = 0; - string aux; - // If the format is 'mmm dd, yyyy' - if (strlen(fmt->buf) > 10) { - // parsing day - i = fmt->find(fmt, ",") - 4; - aux = fmt->substr(fmt, 4, i); - d.day = aux.to_int(&aux); - aux.delete(&aux); - - // parsing month - aux = fmt->substr(fmt, 0, 3); - strcpy(d.month, aux.buf); - aux.delete(&aux); - - // parsing year - i = fmt->find(fmt, ",") + 2; - aux = fmt->substr(fmt, i, (fmt->length(fmt) - i)); - d.year = aux.to_int(&aux); - aux.delete(&aux); - } else { // Assuming format is 'mmm yyyy' - // no day provided in this format - d.day = 0; - - // parsing month - i = fmt->find(fmt, " "); - aux = fmt->substr(fmt, 0, i); - strcpy(d.month, aux.buf); - aux.delete(&aux); - - // parsing year - aux = fmt->substr(fmt, (i + 1), fmt->length(fmt) - (i + 1)); - d.year = aux.to_int(&aux); - aux.delete(&aux); - } - // parsing value - unsigned mon = strmon_to_int(d.month); - d.value = ((d.year * 10000) + (mon * 100) + d.day); - } else { - d.day = 0; - strcpy(d.month, "mmm"); - d.year = 0; - d.value = 0; - } - return d; -} - -// Function to initialize -// an 'empty' game struct -game empty_game(void) -{ - game g; - g.app_id = 0; - g.name = new_string(""); - g.release_date = new_date(NULL); - g.owners = new_string(""); - g.age = 0; - g.price = 0.0f; - g.dlcs = 0; - g.languages = new_strarr(NULL, 0); - g.website = new_string(""); - g.windows = false; - g.mac = false; - g.linux_ = false; - g.upvotes = 0.0f; - g.avg_pt = 0; - g.developers = new_string(""); - g.genres = new_strarr(NULL, 0); - return g; -} - -// Function to initialize a new game with -// the retrievd data in the string array -game new_game(string *s_arr) -{ - game g; - if (s_arr != NULL) { - // App id - g.app_id = atoi(s_arr[0].buf); - // Name - g.name = new_string(s_arr[1].buf); - // Release date - g.release_date = new_date(s_arr + 2); - // Owners - g.owners = new_string(s_arr[3].buf); - // Age, Price, DLCs - g.age = atoi(s_arr[4].buf); - g.price = atof(s_arr[5].buf); - g.dlcs = atoi(s_arr[6].buf); - // Languages - // If string is not empty then splits it into a string array - if (strcmp(s_arr[7].buf, "[]") != 0) { - string tmp = s_arr[7].substr(s_arr + 7, 2, strlen(s_arr[7].buf) - 4); - tmp.replace(&tmp, "', '", "|"); - g.languages = tmp.split(&tmp, "|"); - tmp.delete(&tmp); - } else { - g.languages = new_strarr(NULL, 0); - string *tmp = realloc(g.languages.src, sizeof(string)); - if (tmp != NULL) { - tmp[0] = new_string(""); - g.languages.src = tmp; - g.languages.length = 1; - } - } - // Website - g.website = new_string(s_arr[8].buf); - // Windows, Mac, Linux - g.windows = (strcmp(s_arr[9].buf, "True") == 0) ? true : false; - g.mac = (strcmp(s_arr[10].buf, "True") == 0) ? true : false; - g.linux_ = (strcmp(s_arr[11].buf, "True") == 0) ? true : false; - // Upvotes - float up_votes = atof(s_arr[12].buf); - float down_votes = atof(s_arr[13].buf); - g.upvotes = (up_votes / ((up_votes + down_votes) / 100)); - // Avg pt - g.avg_pt = atoi(s_arr[14].buf); - // Developers - g.developers = new_string(s_arr[15].buf); - // Genres - // If string is not empty and has a ',' then splits it into a string array - if ((strcmp(s_arr[16].buf, "") != 0) && - (s_arr[16].find(s_arr + 16, ",") != strlen(s_arr[16].buf))) { - g.genres = s_arr[16].split(s_arr + 16, ","); - } else { - g.genres = new_strarr(NULL, 0); - string *tmp = realloc(g.genres.src, sizeof(string)); - if (tmp != NULL) { - tmp[0] = new_string(s_arr[16].buf); - g.genres.src = tmp; - g.genres.length = 1; - } - } - } else g = empty_game(); - return g; -} - -// function to deallocates all -// the heap variables inside the game struct -void game_delete(game *g) -{ - if (g != NULL) { - g->name.delete(&g->name); - g->owners.delete(&g->owners); - g->languages.delete(&g->languages); - g->website.delete(&g->website); - g->developers.delete(&g->developers); - g->genres.delete(&g->genres); - } -} - -// Receives a char array that contains -// the data of the game strut in a csv format -// parses it and returns a game data structure -game chararr_to_game(char *ch_arr) -{ - game g; - if (ch_arr != NULL) { - // Initializing static array of strings - // where each index will be a field of the game data struct - string s_arr[GAME_FIELDS_SIZE]; - for (int i = 0; i < GAME_FIELDS_SIZE; i++) { - s_arr[i] = new_string(NULL); - } - - // Initializing variables for the split algorithm - string aux = new_string(NULL); - aux.buf = malloc(sizeof(char) * MAX_LINE_SIZE); - size_t ch_arr_len = strlen(ch_arr), s_arr_i = 0, aux_i = 0; - bool is_str_lit = false; - - // Loop to split each csv cell into a string - // and adds it to the array of strings - for (size_t i = 0; i < ch_arr_len; i++) { - if (ch_arr[i] == '"') { - is_str_lit = (is_str_lit ? false : true); - } else if (ch_arr[i] == ',' && !is_str_lit) { - aux.buf[aux_i] = '\0'; - s_arr[s_arr_i].buf = malloc(sizeof(char) * (strlen(aux.buf) + 1)); - strcpy(s_arr[s_arr_i].buf, aux.buf); - s_arr_i++; aux_i = 0; - } else { - aux.buf[aux_i++] = ch_arr[i]; - } - } - // Adding last string cell to the array - aux.buf[aux_i] = '\0'; - s_arr[s_arr_i].buf = malloc(sizeof(char) * (strlen(aux.buf) + 1)); - strcpy(s_arr[s_arr_i].buf, aux.buf); - aux.delete(&aux); - // Creating game with parsed data - g = new_game(s_arr); - - // Deallocating the string array - for (size_t i = 0; i < GAME_FIELDS_SIZE; i++) { - free(s_arr[i].buf); - } - } else g = empty_game(); - return g; -} - -// Converts avg_pt (int with amount of minutes) -// to a string in format '[x]h [y]m' returns -// a string with 'null' if avg_pt equals zero -char *avgpt_to_chararr (unsigned avg_pt) -{ - char *c_arr = calloc(16, sizeof(char)); - if (avg_pt != 0) { - unsigned h = avg_pt / 60; - unsigned m = avg_pt % 60; - - if (h != 0 && m != 0) { - snprintf(c_arr, 16, "%dh %dm", h, m); - } else if (h != 0) { - snprintf(c_arr, 16, "%dh", h); - } else if (m != 0) { - snprintf(c_arr, 16, "%dm", m); - } - } else { - strcpy(c_arr, "null"); - } - return c_arr; -} - -// prints the game structure in the format -// as is specified in the assignment -void game_print(game *g) -{ - printf("%u %s %s/%d %s %u %.2f %u [", - g->app_id, g->name.buf, - g->release_date.month, - g->release_date.year, - g->owners.buf, g->age, - g->price, g->dlcs); - // languages - for (size_t i = 0; i < g->languages.length - 1; i++) - printf("%s, ", g->languages.src[i].buf); - char *aux = avgpt_to_chararr(g->avg_pt); - printf("%s] %s %s %s %s %.0f%% %s %s [", - g->languages.src[g->languages.length - 1].buf, g->website.buf, - (g->windows ? "true" : "false"), (g->mac ? "true" : "false"), - (g->linux_ ? "true" : "false"), g->upvotes, aux, g->developers.buf); - free(aux); - // genres - for (size_t i = 0; i < g->genres.length - 1; i++) - printf("%s, ", g->genres.src[i].buf); - printf("%s]\n", g->genres.src[g->genres.length - 1].buf); -} - -// Search for the matching 'app_id' and read the game data -// from the file 'games.csv' then adds the game to the games array -void game_read_csv(game *games, size_t *ids, size_t ids_len) -{ - // Handling file opening - FILE *f = fopen(FILE_PATH, "r"); - assert__(f != NULL) { - fprintf(stderr, "Failed to open '%s'\n%s", FILE_PATH, - "Hint: Try adding or removing a '/' in front (OS Dependent)\n"); - } - - char line[MAX_LINE_SIZE + 1]; - size_t cur_id = 0; - // Searching the desired id then calling the parser function - for (size_t i = 0; i < ids_len; i++) { - while (cur_id != ids[i]) { - // reads line from file - fgets(line, MAX_LINE_SIZE, f); - // checking if is a reserved char to then trim it - if (line[strlen(line) - 2] < ' ') line[strlen(line) - 2] = '\0'; - else if (line[strlen(line) - 1] < ' ') line[strlen(line) - 1] = '\0'; - // atoi will only parse the beginning of the string ;) - cur_id = atoi(line); - } - rewind(f); // Resets file pointer to the beginning of the file - games[i] = chararr_to_game(line); // calling parser function - } - fclose(f); // closing the file -} - -// ------------------------------------ main ------------------------------------ // - -// Uses strcmp to check if -// input string equals 'FIM' -// meaning end of input -bool is_fim(char *c_arr) -{ - return (strcmp(c_arr, "FIM") == 0); -} - -// The main function contains the solution to -// the second question of the assignment, which is -// to read 'n' lines of strings containing the -// desired IDs of games to be read from the file 'games.csv', -// parse the data, store it in a game struct already defined, -// and print all of them in a specified format. -int main(void) -{ - // game array - game games[MAX_GAMES]; - size_t games_sz = 0; - // input variables - char input[MAX_FIELD_SIZE ]; - size_t ids[MAX_GAMES]; - - // Reading input and converting to integer - // then adding it to the array of ids to be searched - fgets(input, MAX_LINE_SIZE, stdin); - input[strlen(input) - 1] = '\0'; - while (!is_fim(input)) { - ids[games_sz++] = atoi(input); - fgets(input, MAX_LINE_SIZE, stdin); - // checking if is a reserved char to then trim it - if (input[strlen(input) - 2] < ' ') input[strlen(input) - 2] = '\0'; - else if (input[strlen(input) - 1] < ' ') input[strlen(input) - 1] = '\0'; - } - // If the array of ids is not empty - if (games_sz > 0) { - // Calls function to read from file and populate array of games - game_read_csv(games, ids, games_sz); - - // Prints the games read and frees the heap allocated memory - for (size_t i = 0; i < games_sz; i++) { - game_print(games + i); - game_delete(games + i); - } - } - return 0; -} +// ------------------------------------------------------------------------------ // +// Author: Vinicius Gabriel dos Santos - https://github.com/ravixr +// Sep/2022 +// +// DESCRIPTION: +// In addition to the game data structure and +// the solution to the second question of the assignment, +// this file contains a basic implementation of a string type +// which was extensively used in the game data structure +// as well as in the solution algorithm. +// ------------------------------------------------------------------------------ // + +#include +#include +#include +#include +#include +#include + +#define GAME_FIELDS_SIZE 17 +#define MAX_FIELD_SIZE 500 +#define MAX_LINE_SIZE 1000 +#define MAX_GAMES 100 + +// If on windows remove first slash -> 'tmp/games.csv' +#define FILE_PATH "/tmp/games.csv" + +// Assert macro to print custom message when assertion fails +#define assert__(x) for (; !(x); assert(x)) + +// --------------------------- my_very_basic_string.h --------------------------- // + +typedef struct string_t string; + +typedef struct strarr_t strarr; + +struct string_t { + char *buf; + char (*char_at)(const string *, size_t); + int (*compare)(const string *, const string *); + void (*delete)(string *); + size_t (*find)(const string *, const char *); + size_t (*length)(const string *); + void (*replace)(string *, const char *, const char *); + strarr (*split)(const string *, const char *); + string (*substr)(const string *, size_t, size_t); + float (*to_float)(const string *); + int (*to_int)(const string *); + void (*trim)(string *); +}; + +struct strarr_t { + string *src; + size_t length; + void (*delete)(strarr *); +}; + +// string functions declaration +char str_char_at(const string *, size_t); +int str_compare(const string *, const string *); +void str_delete(string *); +size_t str_find(const string *, const char *); +size_t str_length(const string *); +void str_replace(string *, const char*, const char *); +string new_string(char *); +strarr str_split(const string *, const char *); +string str_substr(const string *, size_t, size_t); +float str_to_float(const string *); +int str_to_int(const string *); +void str_trim(string *); + +// strarr functions declaration +strarr new_strarr(string *, size_t); +void strarr_delete(strarr *); + +// --------------------------- my_very_basic_string.c --------------------------- // + +// Fuction to initialize string type +string new_string(char *src) +{ + string str; + if (src != NULL) { + if (strcmp(src, "") != 0) { + str.buf = malloc(sizeof(char) * (strlen(src) + 1)); + strcpy(str.buf, src); + } else { + str.buf = malloc(sizeof(char) * 5); + strcpy(str.buf, "null"); + } + } else str.buf = NULL; + str.char_at = &str_char_at; + str.compare = &str_compare; + str.delete = &str_delete; + str.find = &str_find; + str.replace = &str_replace; + str.length = &str_length; + str.split = &str_split; + str.substr = &str_substr; + str.to_float = &str_to_float; + str.to_int = &str_to_int; + str.trim = &str_trim; + return str; +} + +// Fuction to deallocate string type +void str_delete(string *self) +{ + free(self->buf); +} + +// Returns character present in the specified index of the string +// e.g: "foo" idx: 0 -> 'f' +char str_char_at(const string *self, size_t idx) +{ + // Macro to assert index is in bounds + assert__(idx < strlen(self->buf)) { + fprintf(stderr, "String index %ld is out of bounds\n", idx); + } + return self->buf[idx]; +} + +// Allows comparison between string type variables using strcmp in the raw data +int str_compare(const string *self, const string *another_str) +{ + return strcmp(self->buf, another_str->buf); +} + +// Searches the string for the first occurrence of the pattern specified. +// Returns the index position of the first character of the pattern +// or the length of the string if the pattern is not found +// e.g.: "foo bar", "bar" -> 4 | "foo bar", "baz" -> 7 +size_t str_find(const string *self, const char *lkf) +{ + size_t i = 0, + self_len = strlen(self->buf), + lkf_len = strlen(lkf); + + // Macro to assert pattern string is valid + assert__((strcmp(lkf, "") != 0) && (lkf_len <= self_len)) { + fprintf(stderr, "Pattern '%s' is invalid for the specified string\n", lkf); + } + + // Find algorithm + for (size_t j = 0; i < self_len; i++) { + if (self->buf[i] == lkf[j]) { + j++; + } else { + i-= j; j = 0; + } + if (j == lkf_len) { + i -= (j - 1); + break; + } + } + return i; +} + +// Returns the length of the string using strlen in the raw data +size_t str_length(const string *self) +{ + return strlen(self->buf); +} + +// Replaces all occurrences of specified pattern in the string with the new specified pattern +// if the string doesn't contains the pattern to be replaced the string is not changed +// e.g.: "foobar", "bar", "" -> "foo" | "foobar", "baz", "" -> "foobar" +void str_replace(string *self, const char *old_val, const char *new_val) +{ + // Saving useful lengths in variables + size_t old_len = strlen(self->buf), + nv_len = strlen(new_val), + ov_len = strlen(old_val); + + // Macro to assert given pattern is valid + assert__((strcmp(old_val, "") != 0) && (old_len >= ov_len)) { + fprintf(stderr, "Invalid pattern for argument 'old_val'\n"); + } + + // Calculating the max possible amount of patterns in the string + // then allocating an array to store their indexes + size_t idx_sz = (old_len / ov_len), + *idx = malloc(sizeof(size_t) * (idx_sz + 1)), + idx_i = 0, last_idx = 0, skip = 0; + + // Getting all starting indexes of where the pattern is found + string aux = new_string(NULL); + aux.buf = (self->buf); + while ((idx_i <= idx_sz) && (last_idx < old_len)) { + idx[idx_i] = str_find(&aux, old_val) + skip; + skip = idx[idx_i++] + ov_len; + aux.buf = (self->buf + skip); + last_idx = idx[idx_i - 1]; + } + + // Checking if the pattern is actually on the string + if (idx[0] != old_len) { + // Assuring 'idx_i' will not be 0 + idx_i > 1 ? (idx_i -= 1) : (idx_i = 1); + + // Calculating new length of the string then allocating + size_t new_len = (old_len + ((nv_len - ov_len) * idx_i)); + char *tmp = malloc(sizeof(char) * (new_len + 1)); + + // Main Replacement algorithm: copies old string char by char, + // when the index is equal to the range of the next found pattern + // starts copying the new pattern char by chat into the string + if (nv_len == 0) nv_len = 1; + for (size_t i = 0, j = 0, k = 0, l = 0; i < new_len;) { + if (k >= idx[l] && k < (idx[l] + ov_len)) { + if (strcmp(new_val, "") != 0) + tmp[i++] = new_val[j]; + j++; + if (j == nv_len) { + k += ov_len; + j = 0; l++; + } + } else tmp[i++] = self->buf[k++]; + } + tmp[new_len] = '\0'; // Adding null terminator in the new string + free(self->buf); // freeing old string + self->buf = tmp; // Pointing to new string + } + free(idx); // freeing array of indexes of the old string +} + +// Uses strtok to split the raw char array +// then build new strings with the splitted char arrays +// and bundle all of them in the strarr type +// e.g.: 'foo,bar' ',' -> ['foo', 'bar'] +strarr str_split(const string *self, const char *delim) +{ + char *str = malloc(sizeof(char) * (strlen(self->buf) + 1)); + strcpy(str, self->buf); + char *token = strtok(str, delim); + size_t count = 0; + strarr str_arr = new_strarr(NULL, 0); + string *tmp; + while(token != NULL) { + tmp = realloc(str_arr.src, sizeof(string) * (count + 1)); + if (tmp != NULL) { + tmp[count++] = new_string(token); + str_arr.src = tmp; + } + token = strtok(NULL, delim); + } + free(str); + free(token); + str_arr.length = count; + return str_arr; +} + +// Returns a new string that is a copy of the portion of the original string +// that starts at character position 'pos' and spans 'len' characters +// e.g.: "foo bar" pos: 4, len: 3 -> "bar" +string str_substr(const string *self, size_t pos, size_t len) +{ + // Macro to assert pos and len are in bounds + assert__((pos + len) <= strlen(self->buf)) { + fprintf(stderr, "String index %ld is out of bounds for '%s'\n", (pos + len), self->buf); + } + string substr = new_string(NULL); + substr.buf = malloc(sizeof(char) * (len + 1)); + for (size_t i = 0, j = 0; i < len; i++, j++) { + substr.buf[j] = self->buf[pos + i]; + } + substr.buf[len] = '\0'; + return substr; +} + +// Uses atof to convert string to float +float str_to_float(const string *self) +{ + return atof(self->buf); +} + +// Uses atoi to convert string to int +int str_to_int(const string *self) +{ + return atoi(self->buf); +} + +// Removes all spaces only from the beginning and ending of the string +// e.g.: " foo " -> "foo" | "foo bar" -> "foo bar" +void str_trim(string *self) +{ + size_t i = 0, j = (strlen(self->buf) - 1); + while (self->buf[i] == ' ' || self->buf[j] == ' ') { + if (self->buf[i] == ' ') i++; + if (self->buf[j] == ' ') j--; + } + string tmp = self->substr(self, i, (j - i) + 1); + free(self->buf); + self->buf = tmp.buf; +} + +// Fuction to initialize string array type +strarr new_strarr(string *src, size_t src_length) +{ + strarr str_arr; + if (src != NULL) + str_arr.src = src; + else { + src_length = (src_length > 0 ? src_length : 1); + str_arr.src = malloc(sizeof(string *)); + } + str_arr.length = src_length; + str_arr.delete = &strarr_delete; + return str_arr; +} + +// Fuction to deallocate string array type +void strarr_delete(strarr *self) +{ + for (size_t i = 0; i < self->length; i++) { + free(self->src[i].buf); + } + free(self->src); +} + +// ----------------------------------- game.h ----------------------------------- // + +typedef struct date_t date; + +struct date_t { + uint8_t day; + char month[4]; + unsigned year; + size_t value; +}; + +date new_date(string *); +unsigned strmon_to_int(const char *); + +typedef struct game_t game; + +struct game_t { + unsigned app_id; + string name; + date release_date; + string owners; + unsigned age; + float price; + unsigned dlcs; + strarr languages; + string website; + bool windows; + bool mac; + bool linux_; + float upvotes; + unsigned avg_pt; + string developers; + strarr genres; +}; + +game empty_game(void); +game new_game(string *); +game str_to_game(string *); +game chararr_to_game(char *); +void game_print(game *); +void game_read_csv(game *, size_t *, size_t); +char *avgpt_to_chararr (unsigned); + +// ----------------------------------- game.c ----------------------------------- // + +// Converts month abbreviated char array in format 'Mmm' +// to unsigned int, if unsuccessful returns 0 +// e.g.: "Aug" -> 8 | "aug" -> 0 +unsigned strmon_to_int(const char *mon) +{ + unsigned i = 0; + if (strcmp(mon, "Jan") == 0) { i = 1; } + else if (strcmp(mon, "Feb") == 0) { i = 2; } + else if (strcmp(mon, "Mar") == 0) { i = 3; } + else if (strcmp(mon, "Apr") == 0) { i = 4; } + else if (strcmp(mon, "May") == 0) { i = 5; } + else if (strcmp(mon, "Jun") == 0) { i = 6; } + else if (strcmp(mon, "Jul") == 0) { i = 7; } + else if (strcmp(mon, "Aug") == 0) { i = 8; } + else if (strcmp(mon, "Sep") == 0) { i = 9; } + else if (strcmp(mon, "Oct") == 0) { i = 10; } + else if (strcmp(mon, "Nov") == 0) { i = 11; } + else if (strcmp(mon, "Dec") == 0) { i = 12; } + return i; +} + +// Fuction to initialize date type +// Assumes that argument is a string +// in the format 'mmm dd, yyyy' or 'mmm yyyy' +// or NULL for empty initialization +date new_date(string *fmt) +{ + date d; + if (fmt != NULL) { + uint8_t i = 0; + string aux; + // If the format is 'mmm dd, yyyy' + if (strlen(fmt->buf) > 10) { + // parsing day + i = fmt->find(fmt, ",") - 4; + aux = fmt->substr(fmt, 4, i); + d.day = aux.to_int(&aux); + aux.delete(&aux); + + // parsing month + aux = fmt->substr(fmt, 0, 3); + strcpy(d.month, aux.buf); + aux.delete(&aux); + + // parsing year + i = fmt->find(fmt, ",") + 2; + aux = fmt->substr(fmt, i, (fmt->length(fmt) - i)); + d.year = aux.to_int(&aux); + aux.delete(&aux); + } else { // Assuming format is 'mmm yyyy' + // no day provided in this format + d.day = 0; + + // parsing month + i = fmt->find(fmt, " "); + aux = fmt->substr(fmt, 0, i); + strcpy(d.month, aux.buf); + aux.delete(&aux); + + // parsing year + aux = fmt->substr(fmt, (i + 1), fmt->length(fmt) - (i + 1)); + d.year = aux.to_int(&aux); + aux.delete(&aux); + } + // parsing value + unsigned mon = strmon_to_int(d.month); + d.value = ((d.year * 10000) + (mon * 100) + d.day); + } else { + d.day = 0; + strcpy(d.month, "mmm"); + d.year = 0; + d.value = 0; + } + return d; +} + +// Function to initialize +// an 'empty' game struct +game empty_game(void) +{ + game g; + g.app_id = 0; + g.name = new_string(""); + g.release_date = new_date(NULL); + g.owners = new_string(""); + g.age = 0; + g.price = 0.0f; + g.dlcs = 0; + g.languages = new_strarr(NULL, 0); + g.website = new_string(""); + g.windows = false; + g.mac = false; + g.linux_ = false; + g.upvotes = 0.0f; + g.avg_pt = 0; + g.developers = new_string(""); + g.genres = new_strarr(NULL, 0); + return g; +} + +// Function to initialize a new game with +// the retrievd data in the string array +game new_game(string *s_arr) +{ + game g; + if (s_arr != NULL) { + // App id + g.app_id = atoi(s_arr[0].buf); + // Name + g.name = new_string(s_arr[1].buf); + // Release date + g.release_date = new_date(s_arr + 2); + // Owners + g.owners = new_string(s_arr[3].buf); + // Age, Price, DLCs + g.age = atoi(s_arr[4].buf); + g.price = atof(s_arr[5].buf); + g.dlcs = atoi(s_arr[6].buf); + // Languages + // If string is not empty then splits it into a string array + if (strcmp(s_arr[7].buf, "[]") != 0) { + string tmp = s_arr[7].substr(s_arr + 7, 2, strlen(s_arr[7].buf) - 4); + tmp.replace(&tmp, "', '", "|"); + g.languages = tmp.split(&tmp, "|"); + tmp.delete(&tmp); + } else { + g.languages = new_strarr(NULL, 0); + string *tmp = realloc(g.languages.src, sizeof(string)); + if (tmp != NULL) { + tmp[0] = new_string(""); + g.languages.src = tmp; + g.languages.length = 1; + } + } + // Website + g.website = new_string(s_arr[8].buf); + // Windows, Mac, Linux + g.windows = (strcmp(s_arr[9].buf, "True") == 0) ? true : false; + g.mac = (strcmp(s_arr[10].buf, "True") == 0) ? true : false; + g.linux_ = (strcmp(s_arr[11].buf, "True") == 0) ? true : false; + // Upvotes + float up_votes = atof(s_arr[12].buf); + float down_votes = atof(s_arr[13].buf); + g.upvotes = (up_votes / ((up_votes + down_votes) / 100)); + // Avg pt + g.avg_pt = atoi(s_arr[14].buf); + // Developers + g.developers = new_string(s_arr[15].buf); + // Genres + // If string is not empty and has a ',' then splits it into a string array + if ((strcmp(s_arr[16].buf, "") != 0) && + (s_arr[16].find(s_arr + 16, ",") != strlen(s_arr[16].buf))) { + g.genres = s_arr[16].split(s_arr + 16, ","); + } else { + g.genres = new_strarr(NULL, 0); + string *tmp = realloc(g.genres.src, sizeof(string)); + if (tmp != NULL) { + tmp[0] = new_string(s_arr[16].buf); + g.genres.src = tmp; + g.genres.length = 1; + } + } + } else g = empty_game(); + return g; +} + +// function to deallocates all +// the heap variables inside the game struct +void game_delete(game *g) +{ + if (g != NULL) { + g->name.delete(&g->name); + g->owners.delete(&g->owners); + g->languages.delete(&g->languages); + g->website.delete(&g->website); + g->developers.delete(&g->developers); + g->genres.delete(&g->genres); + } +} + +// Receives a char array that contains +// the data of the game strut in a csv format +// parses it and returns a game data structure +game chararr_to_game(char *ch_arr) +{ + game g; + if (ch_arr != NULL) { + // Initializing static array of strings + // where each index will be a field of the game data struct + string s_arr[GAME_FIELDS_SIZE]; + for (int i = 0; i < GAME_FIELDS_SIZE; i++) { + s_arr[i] = new_string(NULL); + } + + // Initializing variables for the split algorithm + string aux = new_string(NULL); + aux.buf = malloc(sizeof(char) * MAX_LINE_SIZE); + size_t ch_arr_len = strlen(ch_arr), s_arr_i = 0, aux_i = 0; + bool is_str_lit = false; + + // Loop to split each csv cell into a string + // and adds it to the array of strings + for (size_t i = 0; i < ch_arr_len; i++) { + if (ch_arr[i] == '"') { + is_str_lit = (is_str_lit ? false : true); + } else if (ch_arr[i] == ',' && !is_str_lit) { + aux.buf[aux_i] = '\0'; + s_arr[s_arr_i].buf = malloc(sizeof(char) * (strlen(aux.buf) + 1)); + strcpy(s_arr[s_arr_i].buf, aux.buf); + s_arr_i++; aux_i = 0; + } else { + aux.buf[aux_i++] = ch_arr[i]; + } + } + // Adding last string cell to the array + aux.buf[aux_i] = '\0'; + s_arr[s_arr_i].buf = malloc(sizeof(char) * (strlen(aux.buf) + 1)); + strcpy(s_arr[s_arr_i].buf, aux.buf); + aux.delete(&aux); + // Creating game with parsed data + g = new_game(s_arr); + + // Deallocating the string array + for (size_t i = 0; i < GAME_FIELDS_SIZE; i++) { + free(s_arr[i].buf); + } + } else g = empty_game(); + return g; +} + +// Converts avg_pt (int with amount of minutes) +// to a string in format '[x]h [y]m' returns +// a string with 'null' if avg_pt equals zero +char *avgpt_to_chararr (unsigned avg_pt) +{ + char *c_arr = calloc(16, sizeof(char)); + if (avg_pt != 0) { + unsigned h = avg_pt / 60; + unsigned m = avg_pt % 60; + + if (h != 0 && m != 0) { + snprintf(c_arr, 16, "%dh %dm", h, m); + } else if (h != 0) { + snprintf(c_arr, 16, "%dh", h); + } else if (m != 0) { + snprintf(c_arr, 16, "%dm", m); + } + } else { + strcpy(c_arr, "null"); + } + return c_arr; +} + +// prints the game structure in the format +// as is specified in the assignment +void game_print(game *g) +{ + printf("%u %s %s/%d %s %u %.2f %u [", + g->app_id, g->name.buf, + g->release_date.month, + g->release_date.year, + g->owners.buf, g->age, + g->price, g->dlcs); + // languages + for (size_t i = 0; i < g->languages.length - 1; i++) + printf("%s, ", g->languages.src[i].buf); + char *aux = avgpt_to_chararr(g->avg_pt); + printf("%s] %s %s %s %s %.0f%% %s %s [", + g->languages.src[g->languages.length - 1].buf, g->website.buf, + (g->windows ? "true" : "false"), (g->mac ? "true" : "false"), + (g->linux_ ? "true" : "false"), g->upvotes, aux, g->developers.buf); + free(aux); + // genres + for (size_t i = 0; i < g->genres.length - 1; i++) + printf("%s, ", g->genres.src[i].buf); + printf("%s]\n", g->genres.src[g->genres.length - 1].buf); +} + +// Search for the matching 'app_id' and read the game data +// from the file 'games.csv' then adds the game to the games array +void game_read_csv(game *games, size_t *ids, size_t ids_len) +{ + // Handling file opening + FILE *f = fopen(FILE_PATH, "r"); + assert__(f != NULL) { + fprintf(stderr, "Failed to open '%s'\n%s", FILE_PATH, + "Hint: Try adding or removing a '/' in front (OS Dependent)\n"); + } + + char line[MAX_LINE_SIZE + 1]; + size_t cur_id = 0; + // Searching the desired id then calling the parser function + for (size_t i = 0; i < ids_len; i++) { + while (cur_id != ids[i]) { + // reads line from file + fgets(line, MAX_LINE_SIZE, f); + // checking if is a reserved char to then trim it + if (line[strlen(line) - 2] < ' ') line[strlen(line) - 2] = '\0'; + else if (line[strlen(line) - 1] < ' ') line[strlen(line) - 1] = '\0'; + // atoi will only parse the beginning of the string ;) + cur_id = atoi(line); + } + rewind(f); // Resets file pointer to the beginning of the file + games[i] = chararr_to_game(line); // calling parser function + } + fclose(f); // closing the file +} + +// ------------------------------------ main ------------------------------------ // + +// Uses strcmp to check if +// input string equals 'FIM' +// meaning end of input +bool is_fim(char *c_arr) +{ + return (strcmp(c_arr, "FIM") == 0); +} + +// The main function contains the solution to +// the second question of the assignment, which is +// to read 'n' lines of strings containing the +// desired IDs of games to be read from the file 'games.csv', +// parse the data, store it in a game struct already defined, +// and print all of them in a specified format. +int main(void) +{ + // game array + game games[MAX_GAMES]; + size_t games_sz = 0; + // input variables + char input[MAX_FIELD_SIZE ]; + size_t ids[MAX_GAMES]; + + // Reading input and converting to integer + // then adding it to the array of ids to be searched + fgets(input, MAX_LINE_SIZE, stdin); + input[strlen(input) - 1] = '\0'; + while (!is_fim(input)) { + ids[games_sz++] = atoi(input); + fgets(input, MAX_LINE_SIZE, stdin); + // checking if is a reserved char to then trim it + if (input[strlen(input) - 2] < ' ') input[strlen(input) - 2] = '\0'; + else if (input[strlen(input) - 1] < ' ') input[strlen(input) - 1] = '\0'; + } + // If the array of ids is not empty + if (games_sz > 0) { + // Calls function to read from file and populate array of games + game_read_csv(games, ids, games_sz); + + // Prints the games read and frees the heap allocated memory + for (size_t i = 0; i < games_sz; i++) { + game_print(games + i); + game_delete(games + i); + } + } + return 0; +}