diff --git a/Gemfile b/Gemfile index 8017805..4d5e956 100644 --- a/Gemfile +++ b/Gemfile @@ -5,6 +5,7 @@ ruby "3.2.1" gem "sinatra" gem "sinatra-contrib" +gem "http" # Use Puma as the app server gem "puma", "~> 5.0" diff --git a/Gemfile.lock b/Gemfile.lock index fc6078a..8e17850 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -40,6 +40,7 @@ GEM rexml debug_inspector (1.1.0) diff-lcs (1.5.0) + domain_name (0.6.20240107) draft_matchers (0.0.2) capybara color_namer @@ -51,6 +52,10 @@ GEM ruby2_keywords (>= 0.0.4) faraday-net_http (3.0.2) faraday-retry (1.0.3) + ffi (1.16.3) + ffi-compiler (1.0.1) + ffi (>= 1.0.0) + rake grade_runner (0.0.9) activesupport (>= 2.3.5) faraday-retry (~> 1.0.3) @@ -58,8 +63,19 @@ GEM oj (~> 3.13.12) rake (~> 13) hashdiff (1.0.1) + http (5.1.1) + addressable (~> 2.8) + http-cookie (~> 1.0) + http-form_data (~> 2.2) + llhttp-ffi (~> 0.4.0) + http-cookie (1.0.5) + domain_name (~> 0.5) + http-form_data (2.3.0) i18n (1.12.0) concurrent-ruby (~> 1.0) + llhttp-ffi (0.4.0) + ffi-compiler (~> 1.0) + rake (~> 13.0) matrix (0.4.2) method_source (1.0.0) mini_mime (1.1.5) @@ -164,6 +180,7 @@ DEPENDENCIES capybara draft_matchers grade_runner + http i18n pry puma (~> 5.0) diff --git a/README.md b/README.md index a83f6f8..d65be20 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,66 @@ -# sinatra-template +# OMNICALC 4 -Use this repository to create new Sinatra apps. +This app is an extension of the OMNICALC lessons from previous modules. -Optionally, to use `ActiveRecord` for database operations, add to the `app.rb`: +It features: -```ruby -require "sinatra/activerecord" -``` +* Basic and scientific calculators + +* GPT Chat + +* Hangman and Tic-tac-toe games + +Note : +I wrote this app mostly to see the limitations and strengths of the Ruby language. +It still needs work in a couple of areas : + + * Some of the code should be refactored. + + * Maybe use a different font, so that the images in the games line up better. + +I hope you have as much fun with this app as I had writing it. + +# How to use the app : + +Basic calculator : +------------------ + +You should be able to use this calculator like a normal calculator. + +Scientific calculator : +----------------------- + +This can also be used as a normal calculator with an exception: +The higher-math functions( exponent, factorial ) calls functions from mathjs api. + +Here's the documentation : +https://mathjs.org/docs/reference/functions.html + +Chat GPT : +---------- + +* Ask a question in the text box below the controls and click "SEND". + +* Use the up and down arrows to scroll through the answer. + +Games ( Hangman ) : +------------------- + +* Click on the letter that you want to guess. + +* If the guess is incorrect, a portion of the man will be drawn, if the man is completely drawn, then the game is over. + +* If you correctly guess the word, then you win and the game is over. + +* Click the "RESET" button for a new game. + +Games ( Tic-tac-toe ) : +----------------------- + +* Click any of the yellow buttons below the display, corresponding to the position on the board that you would like to draw your 'X'. + +* The computer will automatically move. + +* Clicking "RESET" will also reset this game. -And in the `config/environment.rb` file add this code block: -```ruby -configure do - # setup a database connection - set(:database, { adapter: "sqlite3", database: "db/development.sqlite3" }) -end -``` diff --git a/app.rb b/app.rb index abbd1c7..2f0bcec 100644 --- a/app.rb +++ b/app.rb @@ -1,9 +1,442 @@ require "sinatra" require "sinatra/reloader" +require "http" +require "json" +require "uri" +require "./libraries/calculator.rb" +require "./libraries/display.rb" +require "./libraries/games.rb" + +MATH_API = "http://api.mathjs.org/v4/?expr=" + +display_size = 12 +lower_display_range = 0 +upper_display_range = 7 + +@display_array = Array.new(display_size) + +@calculator_mode = "basic" + +calc_display = Display.new +calc_object = Calculator.new +calc_tictactoe = Tic_tac_toe.new +calc_hangman = Hangman.new + +calc_start = true + +#======================================================== + +#Test tic-tac-toe grid +game_choice = "tic-tac-toe" +calc_tictactoe.reset_game +calc_tictactoe.render_game_board + + +#======================================================== +#======================================================== get("/") do - " -

Welcome to your Sinatra App!

-

Define some routes in app.rb

- " + + # We do this only once, to welcome the user : + + calc_display.add_to_history("Welcome to OMNICALC4 !!") + calc_display.add_to_history("Have fun !!") + + redirect("/basic") + +end + +#======================================================== + +get("/process_calc/:input_data") do + + input_data = params.fetch("input_data") + + input_array = input_data.split(":") + + calc_input = " " + + calc_input = input_array[0] + calc_mode = input_array[1] + +#------------------------------------------------- +# We will be defining out input processing here : + + case calc_input + + # Basic : + + when "DIV" + calc_input = "/" + + when "MUL" + calc_input = "*" + + when "ADD" + calc_input = "+" + + when "SUB" + calc_input = "-" + + when "PERCENT" + calc_input = "%" + + when "DEC" + calc_input = "." + + # Math functions : + + when "POW" + calc_input = "pow(" + + when "SQR" + calc_input = "sqrt(" + + when "NTHRT" + calc_input = "nthRoots(" + + when "LOG" + calc_input = "log(" + + when "TENPOW" + calc_input = "pow(10," + + when "NEGTENPOW" + calc_input = "pow(10,-" + + when "SIN" + calc_input = "sin(" + + when "COS" + calc_input = "cos(" + + when "TAN" + calc_input = "tan(" + + when "INVSIN" + calc_input = "asin(" + + when "INVCOS" + calc_input = "acos(" + + when "INVTAN" + calc_input = "atan(" + + when "RECIP" + calc_input = "1/" + + when "FACTOR" + calc_input = "factorial(" + + when "MOD" + calc_input = "mod(" + + # GPT + when "UP" + calc_input = "" + + if lower_display_range > 0 + lower_display_range -= 1 + upper_display_range -= 1 + end + + + + when "DOWN" + calc_input = "" + + if upper_display_range < calc_display.get_history_size-1 + lower_display_range += 1 + upper_display_range += 1 + end + + + + # Constants : + when "E" + calc_input = "2.718" + + when "PI" + calc_input = "3.141" + + + # "Auxilliary" buttons : + + when "CLEAR" + calc_display.add_to_history(" ") + calc_input = "" + + when "AC" + calc_display.reset_display + calc_input = "" + + + # Non-numeric characters : + + when "OPENPAREN" + calc_input = "(" + + when "CLOSEPAREN" + calc_input = ")" + + when "COMMA" + calc_input = "," + +#---------------------------------------------------- + + when "EQU" + + expression = calc_display.history_line(upper_display_range) + output = calc_object.calculate(expression) + calc_display.add_to_history(output) + calc_input = "" + + end + +#---------------------------------------------- + if calc_start + calc_display.add_to_history(calc_input) + calc_start = false + + else + calc_display.set_display(upper_display_range, calc_input) + + end + +#---------------------------------------------- + + case calc_mode + + when "BAS" + redirect("/basic") + + when "SCI" + redirect("/scientific") + + when "GPT" + redirect("/gpt") + + end + +end + +#======================================================== + +post("/get_response") do + + gpt_message = params.fetch("gpt_message") + + calc_display.reset_display + + open_ai_api_key = ENV.fetch("OPEN_AI_KEY") + + request_headers_hash = { + "Authorization" => "Bearer #{open_ai_api_key}", + "content-type" => "application/json" + } + + request_body_hash = { + "model" => "gpt-3.5-turbo", + "messages" => [ + { + "role" => "user", + "content" => "#{gpt_message}" + } + ] + } + + request_body_json = JSON.generate(request_body_hash) + + raw_response = HTTP.headers(request_headers_hash).post( + "https://api.openai.com/v1/chat/completions", + :body => request_body_json + ).to_s + + parsed_response = JSON.parse(raw_response, object_class: OpenStruct) + + gpt_response = parsed_response.choices[0].message.content + + # Format and adjust the response on the screen so that it can be + # read by the user. + + calc_display.format_and_display(gpt_response) + + lower_display_range = 0 + + upper_display_range = display_size-1 + + redirect("/gpt") + +end + + +#======================================================== + +get("/basic") do + + upper_display_range = calc_display.get_history_size - 1 + lower_display_range = upper_display_range - display_size + + @calculator_mode = "basic" + + @display_array = calc_display.window(lower_display_range, upper_display_range) + + erb(:basic_calculator) + +end + +#======================================================== + + +get("/scientific") do + + upper_display_range = calc_display.get_history_size - 1 + lower_display_range = upper_display_range - display_size + + @calculator_mode = "scientific" + + @display_array = calc_display.window(lower_display_range, upper_display_range) + + erb(:sci_calculator) + +end + + +#======================================================== + +get("/gpt") do + + @display_array = calc_display.window(lower_display_range, display_size) + + @calculator_mode = "gpt" + + erb(:gpt_calculator) + +end + + +#======================================================== + +get("/games") do + + calc_display.reset_display + + if game_choice == "tic-tac-toe" + + + game_display = calc_tictactoe.render_game_board + + game_state_string = calc_tictactoe.get_game_state + + if game_state_string != "no_wins" + calc_tictactoe.print_message(20, 4, game_state_string) + end + + line_count = 0 + + game_display.each do |display_line| + + calc_display.set_display(line_count, display_line) + line_count += 1 + + end +#.................................... + elsif game_choice == "hangman" + + line_count = 0 + + game_display = calc_hangman.update_screen + + won_or_lost = calc_hangman.win_or_lose + + game_display.each do |display_line| + + calc_display.set_display(line_count, display_line) + line_count += 1 + + end + + end # Of condition block for hangman +#.................................... + + @calculator_mode = "games" + + @display_array = calc_display.window(0, display_size) + + @game_selected = game_choice + + erb(:games_calculator) + +end + +#======================================================== + +get("/reset_game") do + + calc_display.reset_display + + if game_choice == "tic-tac-toe" + + calc_tictactoe.reset_game + calc_tictactoe.render_game_board + + elsif game_choice == "hangman" + + calc_hangman.reset_game + + end + + redirect("/games") + +end + +#======================================================== + +get ("/change_game/:game") do + + calc_display.reset_display + + game_choice = params.fetch("game") + + redirect("/reset_game") + +end + +#======================================================== + +get("/process_move/:row/:column") do + + row = params.fetch("row") + column = params.fetch("column") + + if calc_tictactoe.game_done == "no" + + calc_tictactoe.player_move(row.to_i, column.to_i) + calc_tictactoe.get_game_state + + end + + + if calc_tictactoe.game_done == "no" + + calc_tictactoe.computer_move + calc_tictactoe.get_game_state + + end + + + redirect("/games") + +end + +#======================================================== + +get("/process_choice/:picked_letter") do + + picked_letter = params.fetch("picked_letter") + + calc_hangman.check_picked_letter(picked_letter) + + redirect("/games") + end diff --git a/libraries/calculator.rb b/libraries/calculator.rb new file mode 100644 index 0000000..c851d82 --- /dev/null +++ b/libraries/calculator.rb @@ -0,0 +1,46 @@ +class Calculator + + def initialize + @running_amount = "" + @MATH_API = "http://api.mathjs.org/v4/?expr=" + @parenthesis_status = "CLOSED" + + end # Of method + +#--------------------------------- + + def calculate(expression, precision_setting = 0) + + url_encoded_expression = CGI.escapeURIComponent(expression) + + calculate_url = @MATH_API + url_encoded_expression + + result = HTTP.get(calculate_url).to_s + + @running_amount = result + + result + + end # Of method + +#--------------------------------- + +def get_running_amount() + + @running_amount.to_s + +end + + #--------------------------------- + + def reset_running_amount() + + @running_amount = 0 + @running_amount.to_s + + end + +#--------------------------------- + + +end # Of class diff --git a/libraries/display.rb b/libraries/display.rb new file mode 100644 index 0000000..7b56367 --- /dev/null +++ b/libraries/display.rb @@ -0,0 +1,117 @@ +class Display + + def initialize + # We are going to hardcode some things here : + @history_size = 40 + @ultimate_line_length = 38 + + @display_history = Array.new(@history_size, " ") + + + end + + #------------------------------- + + def window(lower_range, number_of_lines) + + @display_history.slice(lower_range, number_of_lines) + + end + + #------------------------------- + + def set_display(location, display_output, new_line = false) + + if location > @history_size-1 + location = @history_size-1 + end + + if !new_line + @display_history[location] += display_output + + else + @display_history[location] = display_output + end + + end + + #------------------------------- + + def history_line(line_number) + + if line_number >= @history_size + @display_history[@history_size-1] + else + @display_history[line_number] + end + + end + + #------------------------------- + + + def add_to_history(message_string) + + history_counter = 0 + + while history_counter < @history_size do + + temporary_string = @display_history[history_counter+1] + @display_history[history_counter] = temporary_string + history_counter += 1 + + end + + @display_history[history_counter-1] = message_string + + end + + #------------------------------- + + def reset_display() + + end_of_array = @display_history.length()-1 + + for counter in 0..end_of_array + + @display_history[counter] = " " + + end + + end + + #------------------------------- + + def format_and_display(string_to_format) + + line_count = 0 + + while string_to_format.length > @ultimate_line_length do + + temp_string = string_to_format.slice(0, @ultimate_line_length) + + temp_string = temp_string.gsub("\n"," ") + + self.set_display(line_count, temp_string) + + string_to_format = string_to_format.slice(@ultimate_line_length, string_to_format.length) + + line_count += 1 + + end + + self.set_display(line_count, string_to_format) + + line_count + + end + + #------------------------------- + + def get_history_size() + + @history_size + + end + +end # Of class diff --git a/libraries/games.rb b/libraries/games.rb new file mode 100644 index 0000000..33fe0fd --- /dev/null +++ b/libraries/games.rb @@ -0,0 +1,512 @@ +# For Hangman : generate random word : +# https://random-word-api.vercel.app/api?words=1 + +class Games + + def initialize + + @max_x = 38 + @max_y = 12 + @game_display_array = Array.new(@max_y) + @game_done = "no" + + @move_array = [0, 1, 2, 3, 4, 5, 6, 7, 8] + + self.clear_game_grid + + end + +#------------------------------------------------------- + + def render_char_at(x_loc, y_loc, render_char) + + if @max_x > x_loc && @max_y > y_loc + + temp_string = @game_display_array[y_loc] + temp_string[x_loc] = render_char + @game_display_array[y_loc] = temp_string + + end + + + end + +#------------------------------------------------------- + + def print_message(x_loc, y_loc, message) + + x_counter = 0 + + message.each_char do |individual_char| + self.render_char_at(x_loc+x_counter, y_loc, individual_char) + x_counter += 1 + end + + end + +#------------------------------------------------------- + + def clear_game_grid() + + line_counter = 0 + + while line_counter < @max_y do + + @game_display_array[line_counter] = " "*@max_x + line_counter += 1 + + end + + end + +#------------------------------------------------------- + + def get_game_grid() + + @game_display_array + + end + + +end # Of class. + +#============================================================= + +class Tic_tac_toe < Games + + def initialize + + @x_img = Array["X X "," X "] + @o_img = Array[" OO ","O O"] + @game_board = " E000 | E111 | E222 : M000 | M111 | M222 : E000 | E111 | E222 : ------|------|------ : E333 | E444 | E555 : M333 | M444 | M555 : E333 | E444 | E555 : ------|------|------: E666 | E777 | E888 : M666 | M777 | M888 : E666 | E777 | E888 " + @draw_x_loc = 5 + + @move_array = [0, 1, 2, 3, 4, 5, 6, 7, 8] + + @user_move = [] + @computer_move = [] + + @turn = "user" + @game_done = "no" + + super + + end + +#------------------------------------------------------- + + def reset_game + + @move_array = [0, 1, 2, 3, 4, 5, 6, 7, 8] + + @user_move = [] + @computer_move = [] + + @game_board = " E000 | E111 | E222 : M000 | M111 | M222 : E000 | E111 | E222 :------|------|------ : E333 | E444 | E555 : M333 | M444 | M555 : E333 | E444 | E555 :------|------|------: E666 | E777 | E888 : M666 | M777 | M888 : E666 | E777 | E888 " + + + @turn = "user" + + @game_done = "no" + + end + +#------------------------------------------------------- + + def player_move(row, column) + + move_num = row*3+column + + if move_available(move_num) + @user_move << move_num + @move_array.delete(move_num) + @turn = "computer" + end + + end + +#------------------------------------------------------- + + def computer_move + + if @turn == "computer" + + random_number = @move_array.sample + + random_row = random_number / 3 + random_column = random_number % 3 + + @move_array.delete(random_number) + + @computer_move << random_number + + @turn = "user" + + end + + + end + +#------------------------------------------------------- + + def get_game_state + + state_message = "no_wins" + + if is_draw == "yes" + state_message = "Draw" + + elsif is_win(@user_move) == "yes" + state_message = "You win" + + elsif is_win(@computer_move) == "yes" + state_message = "Computer wins" + + end + + state_message + + end # Of method. + +#------------------------------------------------------- + + def is_win(move_array) + + is_winner = "no" + + winning_combos = [[0,1,2],[3,4,5],[6,7,8],[0,3,6],[1,4,7],[2,5,8],[0,4,8],[2,4,6]] + + winning_combos.each do |combo_number_array| + + intersecting_array = move_array & combo_number_array + + if intersecting_array.length == 3 + is_winner = "yes" + @game_done = "yes" + + break + end + + + end # Of outer loop. + + is_winner + + end # Of method. + + +#------------------------------------------------------- + + def is_draw + + game_is_draw = "no" + + if @move_array.length == 0 + game_is_draw = "yes" + @game_done = "yes" + + end + + end # Of method. + +#------------------------------------------------------- + + def move_available(move_number) + + @move_array.include?(move_number) + + end # of method. + +#------------------------------------------------------- + + def render_game_board + + + #Reset board to be re-drawn + + @game_board = " E000 | E111 | E222 : M000 | M111 | M222 : E000 | E111 | E222 :------|------|------ : E333 | E444 | E555 : M333 | M444 | M555 : E333 | E444 | E555 :------|------|------: E666 | E777 | E888 : M666 | M777 | M888 : E666 | E777 | E888 " + + # Render user moves first : + #@user_move + #@computer_move + #@game_board + + @user_move.each do |user_num| + + middle_repl_string = "M"+user_num.to_s*3 + edge_repl_string = "E"+user_num.to_s*3 + + @game_board = @game_board.gsub(middle_repl_string, @x_img[1]) + @game_board = @game_board.gsub(edge_repl_string, @x_img[0]) + + end # Of render loop. + + + # Now the computer moves + + @computer_move.each do |comp_num| + + middle_repl_string = "M"+comp_num.to_s*3 + edge_repl_string = "E"+comp_num.to_s*3 + + @game_board = @game_board.gsub(middle_repl_string, @o_img[1]) + @game_board = @game_board.gsub(edge_repl_string, @o_img[0]) + + end # Of render loop. + + # Finally, clean up : + + counter = 0 + while counter < 9 do + + middle_repl_string = "M"+counter.to_s*3 + edge_repl_string = "E"+counter.to_s*3 + + @game_board = @game_board.gsub(middle_repl_string, " ") + @game_board = @game_board.gsub(edge_repl_string, " ") + + counter += 1 + + end # Of render loop. + + @game_display_array = @game_board.split(":") + + #---------------TEST ONLY ------------------- : + + #@game_display_array.each do |display_line| + # puts "--> #{display_line}" + #end # of test display + + #puts " " + #puts " " + #puts "Movement array ->>>> #{@move_array}" + + @game_display_array + + + end # Of method. + +#------------------------------------------------------- + + def game_done + + @game_done + + end + +#------------------------------------------------------- + + + +end # Of class + +#============================================================= + + +class Hangman < Games + + def initialize + + @word_url = "https://random-word-api.vercel.app/api?words=1" + @hang_counter = 0 + + # For screen real estate purposes, let's limit the word length + # to 14 letters : + @ultimate_word_length = 14 + @correct_guess_counter = 0 + @picked_word = "" + @guessed_word = "" + + super + + end + +#------------------------------------------------------- + + def draw_gallows + + self.print_message(0,0, " |-----|") + + for count in 1..8 + self.print_message(0,count, " |") + end + + self.print_message(0,9, "==|") + + + end + +#------------------------------------------------------- + + def draw_man + + case @hang_counter + when 1 + # Draw head + self.print_message(7, 1, " O ") + self.print_message(7, 2, " O O ") + self.print_message(7, 3, " O ") + + when 2 + # Draw the body + self.print_message(7, 4, " | ") + self.print_message(7, 5, " | ") + self.print_message(7, 6, " | ") + self.print_message(7, 7, " | ") + + when 3 + # Draw the left arm + self.print_message(7, 5, " /| ") + self.print_message(7, 6, "/ | ") + + when 4 + # Draw the right arm + self.print_message(7, 5, " /|\\") + self.print_message(7, 6, "/ | \\") + + when 5 + # Draw the left leg + self.print_message(7, 8, " /") + self.print_message(7, 9, "/ ") + + when 6 + # Draw the right leg + self.print_message(7, 8, " / \\") + self.print_message(7, 9, "/ \\") + end + + + end # Of method + +#------------------------------------------------------- + + def pick_word + + @picked_word = HTTP.get(@word_url).to_s + + word = @picked_word + + word = word.gsub(/[^a-z ]/i, '') + + @picked_word = word + + word_length = word.length + + while word_length > @ultimate_word_length + @picked_word = HTTP.get(@word_url) + end + + temp_string = "" + + word.each_char do |letter| + + temp_string += "_" + + end + + @guessed_word = temp_string + + + end + +#------------------------------------------------------- + + def draw_word + + self.print_message(0, 11, @guessed_word) + + end # Of method + +#------------------------------------------------------- + + def check_picked_letter(picked_letter) + + guessed_correctly = "no" + + picked_letter = picked_letter.upcase + + temporary_word = @picked_word.upcase + temp_guessed_word = @guessed_word + + array_counter = 0 + temporary_word.each_char do |letter| + + if letter == picked_letter + temp_guessed_word[array_counter] = picked_letter + @correct_guess_counter += 1 + guessed_correctly = "yes" + + end + array_counter += 1 + + end # Of loop. + + #........................ + + @guessed_word = temp_guessed_word + + if guessed_correctly == "no" + @hang_counter += 1 + end + + + end + +#------------------------------------------------------- + + def win_or_lose + + win_lose = "dontknow" + + # Check to see if we lost : + if @hang_counter == 6 + win_lose = "lost" + @game_done = "yes" + self.print_message(10, 5, "Sorry,") + self.print_message(10, 6, "You were HANGED!!") + self.print_message(0, 11, @picked_word.upcase) + end + + if @correct_guess_counter == @picked_word.length + win_lose = "won" + @game_done = "yes" + self.print_message(10, 5, "Congratulations,") + self.print_message(10, 6, "No hanging this time!!") + + end + + win_lose + + + end + +#------------------------------------------------------- + + def reset_game + + @hang_counter = 0 + @correct_guess_counter = 0 + pick_word + + self.clear_game_grid + + @game_done = "no" + + + end + +#------------------------------------------------------- + + def update_screen + + # Let's draw the gallows and the unlucky guy : + + draw_gallows + draw_man + draw_word + + @game_display_array + + end # Of method + +end # Of class diff --git a/render.yaml b/render.yaml index 814a0b4..b99513d 100644 --- a/render.yaml +++ b/render.yaml @@ -1,6 +1,6 @@ services: - type: web - name: MYAPPNAME # the name of this service, eg hello-world + name: omnicalc4 # the name of this service, eg hello-world env: ruby # this app is written in ruby plan: free # make sure to set this to free or you'll get billed $$$ buildCommand: "./bin/render-build.sh" # # we already created these two files for you diff --git a/views/basic_calculator.erb b/views/basic_calculator.erb new file mode 100644 index 0000000..4720373 --- /dev/null +++ b/views/basic_calculator.erb @@ -0,0 +1,24 @@ +
+
AC
+
C
+
%
+
/
+
7
+
8
+
9
+
*
+
4
+
5
+
6
+
-
+
1
+
2
+
3
+
+
+
+
+
0
+
.
+
=
+
+ diff --git a/views/fonts/Calculator.ttf b/views/fonts/Calculator.ttf new file mode 100644 index 0000000..8d74251 Binary files /dev/null and b/views/fonts/Calculator.ttf differ diff --git a/views/fonts/SFDigitalReadout-MediumObli.ttf b/views/fonts/SFDigitalReadout-MediumObli.ttf new file mode 100644 index 0000000..ad0656a Binary files /dev/null and b/views/fonts/SFDigitalReadout-MediumObli.ttf differ diff --git a/views/fonts/open-sauce.sans-black.ttf b/views/fonts/open-sauce.sans-black.ttf new file mode 100644 index 0000000..ea2105d Binary files /dev/null and b/views/fonts/open-sauce.sans-black.ttf differ diff --git a/views/games_calculator.erb b/views/games_calculator.erb new file mode 100644 index 0000000..2142e1d --- /dev/null +++ b/views/games_calculator.erb @@ -0,0 +1,78 @@ +
+ +
tic-tac-toe
+
Hangman
+ + +
+ + <% if @game_selected == "tic-tac-toe" %> + +
+ +
*
+
*
+
*
+ + +
*
+
*
+
*
+ + +
*
+
*
+
*
+ + +
+ + <% elsif @game_selected == "hangman" %> + +
+ +
A
+
B
+
C
+
D
+
E
+ +
F
+
G
+
H
+
I
+
J
+ +
K
+
L
+
M
+
N
+
O
+ +
P
+
Q
+
R
+
S
+
T
+ +
U
+
V
+
W
+
X
+
Y
+ +
Z
+ + +
+ + <% end %> + + + +
+ +
RESET
+ + +
diff --git a/views/gpt_calculator.erb b/views/gpt_calculator.erb new file mode 100644 index 0000000..5c013f2 --- /dev/null +++ b/views/gpt_calculator.erb @@ -0,0 +1,13 @@ +
+
+
+ +
+ +
+ + +
+ + +
diff --git a/views/layout.erb b/views/layout.erb index d0e831e..4794f32 100644 --- a/views/layout.erb +++ b/views/layout.erb @@ -1,12 +1,41 @@ - Sinatra Template + OMNICALC 4 + +
+ +
+ + <% @display_array.each do |output| %> + + <% if @calculator_mode == "gpt" || @calculator_mode == "games" %> +
<%= output %>
+ + <% else %> +
<%= output %>
+ + <% end %> + + <% end %> + +
+ +
+
BASIC
+
SCI
+
GPT
+
GAMES
+
+ <%= yield %> + +
+ diff --git a/views/sci_calculator.erb b/views/sci_calculator.erb new file mode 100644 index 0000000..7a0fa01 --- /dev/null +++ b/views/sci_calculator.erb @@ -0,0 +1,62 @@ +
+
x^y
+
√x
+
x√y
+
AC
+
C
+ + +
e
+
π
+
log x
+
10^x
+
10^-x
+ + + +
sin x
+
cos x
+
tan x
+
sin-1 x
+
cos-1 x
+ + + +
tan-1 x
+
,
+
(
+
)
+
/
+ + + +
7
+
8
+
9
+
1/x
+
*
+ + +
4
+
5
+
6
+
x!
+
-
+ + + +
1
+
2
+
3
+
MOD
+
+
+ +
+ + +
+
0
+
.
+
%
+
=
+
diff --git a/views/styles/stylesheet.css b/views/styles/stylesheet.css new file mode 100644 index 0000000..fb5f88a --- /dev/null +++ b/views/styles/stylesheet.css @@ -0,0 +1,221 @@ +@font-face{ + font-family: 'CalculatorFont'; + src: url('../fonts/Calculator.ttf') format('truetype'); +} + +@font-face{ + font-family: 'ButtonFont'; + src: url('open-sauce.sans-black.ttf') format('truetype'); +} + + +/* Calculator body */ + +.calculator { + + border: 5px solid; + border-radius: 5px; + background-color: black; + padding: 10px 10px; + margin: auto; + width: 60%; + + justify-content: center; + width: 400px; +} + +/* LCD display : */ + +.display_area { + border: 1px solid; + background-color: #9fa1a6; + margin-bottom: 10px; +} + +.display { +white-space: pre; +padding: 0px 10px; +text-align: right; +font-family: 'CalculatorFont', Arial, sans-serif; +font-size: 25px; +} + +/* Buttons (Basic calculator) : */ + +a:link, a:visited { + text-decoration: none; + color: black; +} + +.buttons { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 2px; +} + +.bottom_row_buttons { + padding: 2px 0px; + display: grid; + grid-template-columns: 1fr 1fr 2fr; + gap: 2px; +} + +.button { + padding: 20px 0px; + text-align: center; + font-family: 'ButtonFont', Arial, sans-serif; + font-size: 20px; +} + +.all_clear_button { + background-color: red; +} + +.number { + background-color: whitesmoke; +} + +.function { + background-color: yellow; +} + +/* Scientific calculator */ + +.sci_regular_buttons { + display: grid; + grid-template-columns: repeat(5, 1fr); + gap: 2px; +} + +.sci_button { + padding: 20px 0px; + text-align: center; + font-family: 'ButtonFont', Arial, sans-serif; + font-size: 20px; +} + + +.sci_last_row { + padding: 2px 0px; + display: grid; + grid-template-columns: 1fr 1fr 1fr 2fr; + gap: 2px; +} + + +.math_button { + background-color: #6699cc +} + +/* Chat GPT */ + +.controls_buttons { + display: grid; + grid-template-columns: 1fr 3fr 1fr; + gap: 2px; +} + +.control_button_style { + padding: 20px 0px; + margin: 20px; + text-align: center; + font-family: 'ButtonFont', Arial, sans-serif; + font-size: 30px; +} + +.control_color { + background-color:rgb(22, 75, 50) +} + +.clear_control_color { + background-color: yellow; +} + +textarea { + width: 100%; + border-radius: 4px; + background-color: #9fa1a6; + font-size: 25px; + font-family: 'CalculatorFont', Arial, sans-serif; + resize: none; +} + +.send_button { + width: 100%; + padding: 15px 0px; + text-align: center; + font-family: 'ButtonFont', Arial, sans-serif; + font-size: 20px; + background-color: #6699cc + +} + +.gpt_display { + white-space: pre; + padding: 0px 10px; + text-align: left; + font-family: 'CalculatorFont', Arial, sans-serif; + font-size: 25px; + } + +/* Game */ + +.ttt_buttons { + padding: 2px 0px; + display: grid; + grid-template-columns: 1fr 1fr 1fr; + gap: 2px; +} + +.hangman_buttons { + padding: 2px 0px; + display: grid; + grid-template-columns: 1fr 1fr 1fr 1fr 1fr; + gap: 2px; +} + +.game_choice_buttons { + padding: 2px 0px; + display: grid; + grid-template-columns: 1fr 1fr; + gap: 2px; +} + + +.reset_game_button { + padding: 2px 0px; + display: grid; + grid-template-columns: 3fr; + gap: 2px; +} + +/* Calculator mode selection row : */ +.mode_selection_area { + padding: 10px 5px; + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 2px; +} + +.mode_buttons { + padding: 20px 0px; + text-align: center; + font-family: 'ButtonFont', Arial, sans-serif; + font-size: 20px; +} + +.basic_button { + background-color: teal; +} + +.scientific_button { + background-color: blue; +} + +.gpt_button { + background-color: green; +} + +.games_button { + background-color: orangered; +}