Make a Game. Win a Bitcoin.

00
Days
00
Hours
00
Mins
00
Secs

Until Jam Ends on Apr 18

Learn to Code via Tutorials on Repl.it

Posts
โ–ฒ
30
๐Ÿš€ A Starter Guide to Pygame ๐Ÿ“€
# ๐Ÿš€ A Starter Guide to Pygame ๐Ÿ“€ Pygame is an open-source library for making graphical applications with Python. Learn more about it on the official [website](https://pygame.org). This tutorial is intended to help you setup a very basic Pygame interface. It's for Python beginners or people who want to quickly bootstrap a Pygame project. Find more about Repl.it's GFX public beta announcement [here](https://repl.it/talk/announcements/Replit-GFX-Public-Beta-Build-Games-and-GUI-Apps/11545). So let's get started! We're going to be building a simple screen saver. The final result will look like the following ![programProgress4](https://i.imgur.com/dEileMP.gif) ## Starting out When creating a new project, be sure to select "PyGame" when creating a project. ![image](https://storage.googleapis.com/replit/images/1552705106070_4aa0e807cd3fb5197a7251222d74d724.pn) Now we're ready to start writing some code! ## Painting the Screen Red ๐ŸŽจ First, we're importing Pygame and initializing all the imported pygame modules with [`pygame.init()`](https://www.pygame.org/docs/ref/pygame.html#pygame.init). Second, we're declaring `width` and `height` variables that represent the size of the screen. Third, we're setting `backgroundColor` to a tuple of 3 elements that represent the RGB numbers. Fourth, `screen` is a display Surface, or an initialized window or screen for display with the [`set_mode()`](https://www.pygame.org/docs/ref/display.html#pygame.display.set_mode) method. Lastly, in the infinite loop, we're filling the screen to the `backGroundColor` and updatiang the display Surface to the screen. In other words [`pygame.display.flip()`](https://www.pygame.org/docs/ref/display.html#pygame.display.flip) "refreshes" the screen with changes you made to the grahpics. ```python import pygame pygame.init() width, height = 800, 600 backgroundColor = 255, 0, 0 screen = pygame.display.set_mode((width, height)) while True: screen.fill(backgroundColor) pygame.display.flip() ``` So, we get a red background! ![programProgress1](https://storage.googleapis.com/replit/images/1552448696584_73e029d5335f2d2a06c92a180e7103be.pn) ## Adding the DVD Right before you while loop, import an image of your DVD. Feel free to use the same image I used, in the repl below. ```python dvdLogo = pygame.image.load("dvd-logo-white.png") ``` Then, create a rectangle from the Surface, or from the image you just loaded with [`get_rect()`](https://www.pygame.org/docs/ref/surface.html?highlight=get_rect#pygame.Surface.get_rect) ```python dvdLogoRect = dvdLogo.get_rect() ``` Now, inside of the `while` loop (after filling the background color), "map" the imported image to the `dvdLogoRect` rectangle using the [`blit()`](https://www.pygame.org/docs/ref/surface.html?highlight=blit#pygame.Surface.blit) method. That way, the image stays inside of the invisible `dvdLogoRect` rectangle. ```python screen.blit(dvdLogo, dvdLogoRect) ``` Now, we get a DVD! ![programProgress2](https://storage.googleapis.com/replit/images/1552449702561_27872d2c7a30d4d9dbb53d3c908851d9.pn) ## Moving the DVD Logo To move the DVD logo, simply use `move()` by a speed: ```python dvdLogoRect = dvdLogoRect.move(dvdLogoSpeed) ``` Make sure you also declare and initialie dvdLogoSpeed at the top of the file. `dvdLogoSpeed[0]` represents the speed in the `x` direction. ```python dvdLogoSpeed = [1, 1] ``` Finally, I added a `time.sleep(10 / 1000)` so the logo moves slower. So my code ended up looking like ![programProgress3](https://i.imgur.com/dsh8TYo.gif) The DVD logo *will* move off the screen because there is no bouncing. To implement a check for bouncing, add the following: ```python if dvdLogoRect.left < 0 or dvdLogoRect.right > width: dvdLogoSpeed[0] = -dvdLogoSpeed[0] if dvdLogoRect.top < 0 or dvdLogoRect.bottom > height: dvdLogoSpeed[1] = -dvdLogoSpeed[1] ``` The `.left` and `.right` properties don't seem to be [documented](https://www.pygame.org/docs/ref/rect.html?highlight=rect%20left), but it's implied that `.left` measures the distance from the left part of the `dvdLogoRect` Rect (rectangle) to the left part of the `screen.` And so on and so fourth for `.right`, `.top`, and `.bottom`. Here is the final result ![programProgress4](https://i.imgur.com/dEileMP.gif) Now let me know when the logo hits the corner!
18
posted by eankeen (48) 10 days ago
โ–ฒ
8
Make your first Pygame ๐ŸŽฎ
## Make games the easy way, and forget about the setup ๐Ÿ˜Œ ![](https://tiresome-aunt.surge.sh/ezgif-9aa456d6-a2de-4d54-a2b3-6828c507544d.com-video-to-gif_(1).gif) [Demo + Code](https://repl.it/@jajoosam/paddleBasket) โฏ๏ธ ๐Ÿ‘จโ€๐Ÿ’ป I've heard coding in python is quite delightful! And I agreed once I made my first python app that didn't just run on the terminal - a game made with [Pygame](https://www.pygame.org/). You'd usually have to spend a while getting set up, installing Pygame, dependencies and then have to spend time compiling an executable before sharing your game. But with [replit's new GFX system](https://repl.it/talk/announcements/Replit-GFX-Public-Beta-Build-Games-and-GUI-Apps/11545) - there's absolutely no need for that โœŒ๏ธ This is a tutorial to get started with Pygame, and make a simple game within 30 minutes! ## ๐Ÿ› ๏ธ Getting our environment running Head over to [repl.it](http://repl.it) and once you're logged in, hit `new repl โ†’ Pygame` to create the repl where we're going to be making our game. ![](https://tiresome-aunt.surge.sh/-7158aee6-09d3-41ef-a559-42f5cb010b22untitled) That's it ๐Ÿ˜‰ ## ๐ŸŽฒ Understanding the game Before we start coding, let's understand what we're making ๐Ÿ› ๏ธ Our game concept is pretty straightforward - there's a paddle - a simple rectangle that moves left or right in the screen, attempting to catch balls falling from the top of the screen. A higher score is more number of balls caught โšพ ## ๐Ÿ‘จโ€๐Ÿ’ป Initializing pygame You can go right ahead and paste this code in your repl! ```python # adding libraries we're going to use import pygame from random import randint pygame.init() # initializing variables to account for the number of balls caught, and total dropped score = 0 total = 0 myfont = pygame.font.SysFont('monospace', 50) # creating a font to write the score in # Making dictionaries with settings for everything. display = { "width": 800, "height": 600 } paddle = { "width": 200, "height": 20, "x": 300, "y": 580, "velocity": 10 } ball = { "radius": 15, "y": 30, "x": randint(0, display["width"]), "velocity": 20 } # creating a window, and launching our game win = pygame.display.set_mode((display["width"], display["height"])) # 800 width, 600 height ``` I've added comments to explain what each line does ๐Ÿ˜„ Make sure to paste in the dictionaries too, they'll be super useful soon! ## ๐Ÿ‘พ The paddle Our paddle is going to be just a little rectangle that moves when we hit the arrow keys. Before we can start making it, we need to create the main loop. Pygame will run the code inside this loop continuously, to update the screen based on inputs. Paste all this in! ```python while True: pygame.time.delay(100) win.fill((255, 255, 255)) for event in pygame.event.get(): if event.type == pygame.QUIT: break keys = pygame.key.get_pressed() if keys[pygame.K_LEFT]: paddle["x"] -= paddle["velocity"] if keys[pygame.K_RIGHT]: paddle["x"] += paddle["velocity"] pygame.draw.rect(win, (255, 0, 0), (paddle["x"], paddle["y"], paddle["width"], paddle["height"])) pygame.display.update() ``` โ€‹ ```python pygame.quit() ``` Don't worry, I'm not just going to leave you like that xD Let's break this up into smaller blocks to explain what everything does! Let's talk about everything inside the while loop, which will run forever - since `True` will always remain `True` ๐Ÿ˜ฎ ```python pygame.time.delay(100) win.fill((255, 255, 255)) ``` We're adding the delay so that the loop doesn't run too often, and there's some gap between each cycle - keeping our repl from crashing. `100` is delay in milliseconds, causing the loop to run 10 times a second. `win.fill()` takes a color in `RGB` as it's argument - and `255, 255, 255` represents white, filling our window with white before we draw anything onto it ๐Ÿ–Œ๏ธ ```python for event in pygame.event.get(): if event.type == pygame.QUIT: break ``` This piece of code goes over all events that pygame gives us, and breaks the loop if Pygame has been quit. When the loop breaks, we go to the line which says `pygame.quit()`- you know what that does ๐Ÿ˜› ```python keys = pygame.key.get_pressed() if keys[pygame.K_LEFT]: paddle["x"] -= paddle["velocity"] if keys[pygame.K_RIGHT]: paddle["x"] += paddle["velocity"] ``` To get this - lets first clear out our understanding about the coordinate grid - it doesn't start at the center in pygame! In fact, the top left corner is `0, 0` and `x` increases as you go right, while `y` increases as you move down. This block of code gets all currently pressed keys, and checks whether the left or right keys are pressed. If they are, it changes the `x` coordinates of the paddle - reducing if โฌ…๏ธ is pressed, and increasing if โžก๏ธ is pressed by the velocity we set in the `paddle` dictionary. Try changing the `velocity` to see what happens ๐Ÿค” ```python pygame.draw.rect(win, (255, 0, 0), (paddle["x"], paddle["y"], paddle["width"], paddle["height"])) pygame.display.update() ``` This is where we actually draw our paddle to the screen - in the window called `win`, red in color (`255, 0, 0` RGB) - at `paddle[x]` on the x axis, and `paddle[y]` on the y axis. We've also set the width and height in the `paddle` dictionary, feel free to mess around with it! Finally, `pygame.display.update()` updates the entire screen with what we've drawn in this cycle of the loop! Try running the code, and hitting the left and right arrow keys! You should see a little rectangle moving around ๐Ÿ‘‡ ![](https://tiresome-aunt.surge.sh/CleanShot_2019-03-13_at_19-ac9eb692-26e2-421e-b279-84978459c836.38.32.gif) ## โšฝ Generating falling circles Let's bring up the `ball` dictionary up again ๐Ÿ‘‡ ```python ball = { "radius": 15, "y": 30, "x": randint(0, display["width"]), "velocity": 20 } ``` What does the `"x"` line do? We're selecting a random x co-ordinate between `0` and `display["width"]` (currently 800) - using the `randint` function we imported right at the start of our code. Add this inside your while loop, right before you draw the paddle to the screen: ```python ball["y"] += ball["velocity"] pygame.draw.circle(win, (0, 0, 255), (ball["x"], ball["y"]), ball["radius"]) ``` We're increasing the y co-ordinate of the ball by its `velocity` and drawing the ball again in every cycle of the loop. ## ๐Ÿ† When do you actually score a point, though? The final part of our game would be checking if the ball hits the paddle when it's at the bottom of the screen. Collision detection is going to be essential to most of the games you're going to make in the future, so let's go over it here! ```python if ball["y"] + ball["radius"] >= paddle["y"]: if ball["x"] > paddle["x"] and ball["x"] < paddle["x"] + paddle["width"]: score += 1 total += 1 ball["y"] = 0 ball["x"] = randint(0, display["width"]) ``` First up, we're learning if the ball has hit the level of the paddle - by checking if the ball's radius + it's position on the y axis is equal to the position of the paddle. ```python if ball["x"] > paddle["x"] and ball["x"] < paddle["x"] + paddle["width"] ``` With this long condition, we're testing if - The ball's position on the X axis is greater than the paddle's position on the X axis AND - The ball's position on the X axis is lesser than the sum of the paddle's position on the X axis and its width If this condition is true, it means that the ball has landed on the paddle, and we increase the score ๐Ÿ™Œ Maybe this image helps a bit ๐Ÿคž ![](https://tiresome-aunt.surge.sh/-98fcc7ff-0ac4-4a09-8d46-2e06394d73b8untitled) After this, regardless of whether a point has beens scored or not, we add one to the total number of balls landed - and reset the ball's position, setting the ball's y co-ordinate to 0, and generate a random position for the X axis. Lastly, we're going to write the score on the screen ๐Ÿ… ```python textsurface = myfont.render("score: {0}/{1}".format(score, total), False, (0, 0, 0)) win.blit(textsurface, (10, 10)) ``` We create a new [surface](https://www.pygame.org/docs/ref/surface.html) where we write the text using python's [format](https://www.programiz.com/python-programming/methods/string/format) function, replacing `{0}` with the socre, and `{1}` with the total. We're writing this in black (`0, 0, 0` RGB). `win.blit(textsurface, (10, 10))` merges the text with the main window, at co-ordinates `10, 10`. And that's the game - the full thing ๐Ÿคฏ ## ๐Ÿ”ฎ Things to try - Changing the contents of the dictionary and seeing what happens โ“ - Make multiple balls fall at the same time ๐Ÿ”ด ๐Ÿ”ต โšซ - Make the paddle move up and down too ๐Ÿš€ - Adding poison balls - the game stops when your paddle hits one ๐Ÿ˜ต Be sure to put down any questions or improvements down in the comments ๐Ÿ’ฌ And here's all the code for you to go over again - I highly recommend going through this to help you understand the flow of the program, and the sequencing of everything if you were even a little confused by this guide ๐Ÿ˜… https://repl.it/@jajoosam/paddleBasket
0
posted by jajoosam (410) 9 days ago
โ–ฒ
8
A Slightly More Advanced ChatBot [Intermediate, Python]
# Hello Fellow Replers! Welcome to my next tutorial with a ChatBot, if you haven't seen my last tutorial, then you should [click here](https://repl.it/talk/learn/Simple-Chat-Bot-Beginner-Level-Python/11130). # How do we make our ChatBot more advanced? That is easy to answer first we need to think, how would this be possible? With connecting our ChatBot to google and find the most relevant answer to the User Input? Good idea, but no. In fact we can make even simpler, and I'm going to explain. We can use two lists! Now this is really cool, and simple. A lot of people don't know how to do this. You will after reading the rest of this tutorial. # How can we use a list to detect User Input? Well, we can't do that, but we can use it so the ChatBot can figure out how to respond to the User. Let me give you a small explanation about how this is possible. First, we have two lists: `Check` `Respond` If we assign values then they will look like this: ```python Check = ["hello", "how are you?"] Respond = ["Hi.", "I'm good.'] ``` Do you notice anything? The User Inputs and the Responses have the same List Index Value(LIV). Let me show you. If you look at list `Check` you will see the first item: `hello` this has a LIV of `0`. And if you look at list `Respond` you will see the first item: `Hi` this also has a LIV of `0`. Do you see a correlation between the two? Maybe you do, maybe you don't you will understand later in the next section. # Now to the code! Alright so we have the basic code we wrote in the last tutorial. That code was simple, and impractical. Let's simplify it. Here is the code: ```python UserInput = "" ChatBot_Name = "Ozzy" UserInput = str.lower(input(": ")) if UserInput == "hi": print(ChatBot_Name + ": Hello!") elif UserInput == "who made you?": print(ChatBot_Name + ": I was made by Christian Thone") ``` Lets first create a new file, you can do that by clicking the little paper icon on the side of you project: ![Screenshot 2019-02-26 at 8.17.57 AM](https://storage.googleapis.com/replit/images/1551187092327_54d076adc829d1b11f9b6abac92c3275.pn) Now go to the smaller paper Icon with the plus sign and click that. Now you have created a file, for the name of your file name it `client.py`. There is no specific reason for the name it just looks cool. Ok, back to the tutorial. Inside of `client.py` create the following lists: ```python User_Inputs = [] __Responses = [] ``` Now obviously we have nothing in the lists, but that is what we want right now. Hmmm? So how do we use these lists? Easy! We use what Repl.it has called ```import```. Type the following into `main.py` at the top of the script: ```python from client import User_Inputs, __Responses ``` Now we can use these lists. We need some values in these lists for anything to happen, so write the following into your lists in `client.py`: ```python # REMEMBER TO HAVE YOUR USER_INPUTS IN LOWERCASE! User_Inputs = ["hi"] __Responses = ["Hello."] ``` Okay cool, now we have some simple code for our lists, but we still cant do anything with our ChatBot yet. First, we need to tell it what to do. Go into `main.py` and type the following: ```python if UserInput in User_Inputs: index = User_Inputs.index(UserInput) print(ChatBot_Name + __Responses[index]) ``` Is the LIV thing making sense to you? If not I will explain it now, skip this if you understand it So basically I am using the LIV, to tell the ChatBot how to respond to user input. Look at this and you might understand. In the lists we have two different, items. `hi`, `hello` The easiest way to look at this is by understanding that they are in the same position just a different list. Here is a visual ['hi'] | 0 ['Hello.'] They match up with the same index that is why I can print the index of `User_Inputs`, through `__Responses`. Now here is the finished output: ![Screenshot 2019-02-26 at 8.38.51 AM](https://storage.googleapis.com/replit/images/1551188357482_f1285e9a35d77c937492742df8e7b616.pn) If you have any questions then post them in the comments. Stay tuned for "An Advanced ChatBot [Advanced, Python]", I will show you how to have your bot scan User Input for Keywords!. Keep coding, Christian Thone @ChristianThone
16
posted by ChristianThone (26) 24 days ago
โ–ฒ
2
๐Ÿš€ Using Webpack To Bootstrap A JS App (Webpack Dev Server)๐Ÿ’Ž
# ๐Ÿš€ Using Webpack To Bootstrap A JS App (Webpack Dev Server)๐Ÿ’Ž Webpack is an integral part of modern JavaScript tooling. It bundles all your JavaScript modules and has plugins for bundling CSS, JSX, and many other files. It's used for creating React and Vue single page applications and much more! We're going to be manually configuring Webpack to work using the official `webpack-dev-server`! ### Prerequisites - Intermediate experience with JavaScript - Understand basic Webpack concepts Note that a few days ago I created a Webpack tutorial [here](https://repl.it/talk/learn/Using-Webpack-To-Bootstrap-A-JS-App-Express/11929), but I used Express among other middleware and tooling. After I made that tutorial, I realized that it's definitely possible after studying the config of an ejected create-react-app. So I would recommend using this approach as it is easier. I typically wouldn't be posting such a similar tutorial in a small amount of time, but this method is truly more flexible and less confusing. # Getting Started Typically we create our projects (in development) using [webpack-dev-server](https://github.com/webpack/webpack-dev-server). We're going to be configuring and creating a quick Webpack development server. ![webpack](https://i.imgur.com/e0XFRGh.gif) ## Creating the Configuration Let's start off with requiring the packages and configuration files we need. ```js // index.js let path = require("path"); let webpack = require("webpack"); let webpackDevServer = require("webpack-dev-server"); let webpackConfig = require("./webpack.config"); ``` Remember to add `express` and `webpack-dev-server` as a dependency. Now, let's create that `webpack.config.js` configuration file. We're going to be including all the standard stuff. Be sure to add `friendly-errors-webpack-plugin` and `html-webpack-plugin` to your `package.json`. Most of this configuration is directly copied from the Express Webpack [tutorial](https://repl.it/talk/learn/Using-Webpack-To-Bootstrap-A-JS-App-Express/11929) I posted recently. ```js // webpack.config.js const path = require("path"); const webpack = require("webpack"); const FriendlyErrorsWebpackPlugin = require("friendly-errors-webpack-plugin"); const HtmlWebpackPlugin = require("html-webpack-plugin"); module.exports = { mode: "development", devtool: "cheap-module-eval-source-map", entry: { main: path.resolve(process.cwd(), "src", "main.js") }, output: { path: path.resolve(process.cwd(), "dist"), publicPath: "/" }, watchOptions: { // ignored: /node_modules/, aggregateTimeout: 300, // After seeing an edit, wait .3 seconds to recompile poll: 500 // Check for edits every 5 seconds }, plugins: [ new FriendlyErrorsWebpackPlugin(), new webpack.ProgressPlugin(), new HtmlWebpackPlugin({ template: path.resolve(process.cwd(), "public", "index.html") }) ] } ``` - [`friendly-errors-webpack-plugin`](https://github.com/geowarin/friendly-errors-webpack-plugin) outputs more helpful Webpack errors - [`html-webpack-plugin`](https://webpack.js.org/plugins/html-webpack-plugin) creates an HTML5 file and injects `<script></script>`tags with our Webpack bundles. Recall in our config we created a bundle called `main`, which is resolved to `./src/main.js`(this is the Webpack bundle we're injecting). And, because we're adding a `template` property, it means that `html-webpack-plugin` will use our HTML file, then inject the script tags - the `new webpack.ProgressPlugin(),` inside `plugins: []` gives Webpack's progress building your files (in the terminal) - I chose `cheap-module-eval-source-map` for the property `devtool` because it provides fast compilation with source maps. Read more in the [documentation](https://webpack.js.org/configuration/devtool/#root) - `watchOptions` prevents Webpack from recompiling everything too much (and lagging your replit). See the inline comments and [documentation](https://webpack.js.org/configuration/watch) for more info Now, let's create our `src/main.js` and `public/index.html` that we referenced in the Webpack config! ## Creating our entrypoints For the `index.html`, I've included all the standard HTML boilerplate with the addition of a `div` with an `id` of `app`. ```html <!-- public/index.html --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Simple Webpack Boilerplate</title> </head> <body> <div id="app"></div> </body> </html> ``` For the `main.js` I do some simple DOM manipulation and logging. This is where your application really starts. ```js // src/main.js console.log("Juliett Kilo Lima"); document.getElementById("app").innerHTML = "Mike November Oscar"; ``` ## Creating the Webpack Development Server Now, this won't do anything if we don't create the Webpack development server or use our Webpack configuration. So let's do that. First, we must create a configuration object for our `webpack-dev-server`. It's exactly the same as the `devServer` property on a typical `webpack.config.js`. I used the [documentation](https://webpack.js.org/configuration/dev-server) to make a simple configuration. ```js let webpackDevServerOptions = { publicPath: "/", contentBase: path.join(process.cwd(), "dist"), historyApiFallback: true, hot: true, host: "0.0.0.0", allowedHosts: [ ".repl.it", ".repl.co", ".repl.run" ] }; ``` Some things worth mentioning - [`historyApiFallback: true`](https://webpack.js.org/configuration/dev-server/#devserverhistoryapifallback) makes `webpack-dev-server` return your `index.html` where there would typically be a 404 error. (ex. if you try to access `/other/route`) It's very useful when creating a Single Page Application (SPA) - [`hot: true`](https://webpack.js.org/configuration/dev-server/#devserverhot) causes the website to automatically update right after you save your files - [`host: "0.0.0.0"`](https://webpack.js.org/configuration/dev-server/#devserverhost) makes the Webpack development server accessable locally, outside `localhost` - [`allowedHosts`](https://webpack.js.org/configuration/dev-server/#devserverallowedhosts) is a whitelist of sites that can access the dev server. A prepended `.` means all the subdomains of that particular domain are whitelisted Second, we're going to use our Webpack configuration to create the dev server. I couldn't find any solid documentation on this, but you can find more example code related to this [here](https://github.com/webpack/webpack-dev-server/tree/4a0486456753cbf57a251822d70454748ca7ffe3/examples/api/simple). ```js webpackDevServer.addDevServerEntrypoints(webpackConfig, webpackDevServerOptions); let webpackCompiler = webpack(webpackConfig); let app = new webpackDevServer(webpackCompiler, webpackDevServerOptions); ``` Then, make the server listen on a port ```js let port = process.env.PORT || 3000; app.listen(port, () => console.log(`App listening on ${port}`)); ``` So, our final `index.js` should look like the following ```js let path = require("path"); let webpack = require("webpack"); let webpackDevServer = require("webpack-dev-server"); let webpackConfig = require("./webpack.config"); let webpackDevServerOptions = { publicPath: "/", contentBase: path.join(process.cwd(), "dist"), historyApiFallback: true, hot: true, host: "0.0.0.0", allowedHosts: [ ".repl.it", ".repl.co", ".repl.run" ] }; webpackDevServer.addDevServerEntrypoints(webpackConfig, webpackDevServerOptions); let webpackCompiler = webpack(webpackConfig); let app = new webpackDevServer(webpackCompiler, webpackDevServerOptions); let port = process.env.PORT || 3000; app.listen(port, () => console.log(`App listening on ${port}`)); ``` And huzzah! After running, we get "Mike November Oscar" on the web page. ![finalResult](https://storage.googleapis.com/replit/images/1553221771034_0a8f8c2b9a9ebe2dcdc7f630ffdad63f.pn) This is where the tutorial ends but where your Webpack application starts! Now you can use whatever plugins and loaders you need to create your applications!
0
posted by eankeen (48) 12 hours ago
โ–ฒ
7
Building Word Clouds from the News using Python and Repl.it
# Building word clouds Hey all, I wrote a tutorial on how to [scrape news sites using RSS and transform the articles into word clouds](https://www.codementor.io/garethdwyer/building-news-word-clouds-using-python-and-repl-it-sy7l88roq) for quick and easy viewing. Shout if you have any questions or feedback! ![Demo](https://storage.googleapis.com/replit/images/1552380585411_379051d505091c2a47fa976406b1478c.pn)
1
posted by GarethDwyer1 (88) 10 days ago
โ–ฒ
105
Making a Phaser Game with HTML5 and JavaScript
# Making a Phaser Game with HTML5 and JavaScript Hi guys! Everybody likes to make games, right? And everybody has their own way of doing it. In this tutorial I wanted to share a very simple, yet effective way to make games in your browser! It should be easy enough for most people with javascript knowledge to follow along and, if you want to investigate further, there are endless possibilities out there! ![image](https://storage.googleapis.com/replit/images/1539468462288_75406edb94f04ca097cc5b4705ccbc85.pn) ### Phaser As Phaser describes itself it is a fast, free and fun open source framework for Canvas and WebGL powered browser games. And it really is! It is super simple to use and is quite easy to set up. No super extensive javascript knowledge is necessary and the process of making games is fun and rewarding. It also comes with tons of extra features that you may need in some more complicated games so while it caters to starters as well, it also does not lack depth if you want to look further. Anything from image editing to complex game mechanic mathematics is possible. ##### Sites to use The official Phaser website is [here](https://phaser.io). Additionally, because we are going to use Phaser 3, the latest release, the examples on the site will most probably not work for v3. If you want some examples of v3 features the link is [here](https://labs.phaser.io/). You should not need the examples during this tutorial but if you want to learn further that is where you start. Google works as well but be careful about which version is being discussed. Version 3 is relatively new and v2 has loads more documentation and examples and tutorials on it. However, I would recommend learning v3 because it is generally better in many ways and the knowledge will last you longer and it will be more current. #### Prerequisites (what you need before doing this tutorial) The pre-requisites are: * A basic understanding of HTML, CSS and Javascript. * Knowledge in Javascript about the `this` keyword. * Some time and patience. * 3 rolls of duct tape. * Lots of cardboard * Creativity ### Let's Get Started! The repl.it project that I will be using for this tutorial is [here](https://repl.it/@kaldisberzins/Phaser-Tutorial) and the website for it if you just wanna play the game is [here](https://phaser-tutorial.kaldisberzins.repl.co/). If you ever get stumped on a step that I take in this tutorial just check the repl and see how the code looks in it. If all else fails a bit of copy-paste will solve your issues. Make a new HTML/CSS/JS repl and follow along... So, first of all we need to include the Phaser script into our website. The only piece of HTML in this tutorial will be the following:`<script src="//cdn.jsdelivr.net/npm/[email protected]/dist/phaser.min.js"></script>`Just paste this into your project's HTML file right above your script tag that links to `script.js`. The order is important and if you get it wrong nothing will work. If your project is not working you should definitely have a look at the order of your scripts. The Phaser script should be first. With that out of the way, let's get into making our game! The first bit of code is a standard template that is in most simple Phaser games (more advanced ones may use a slightly different structure but the idea is the same). The code looks like this: ```javascript let config = { type: Phaser.AUTO, width: 800, height: 500, physics: { default: 'arcade', arcade: { debug: false } }, scene: { preload: preload, create: create, update: update } }; const game = new Phaser.Game(config); function preload(){ } function create(){ } function update(){ } ``` While this may look alien to you, don't stress. To follow along this tutorial you don't need to understand what everything does exactly. The main things you should pay attention to are: * The three functions at the bottom `preload`, `create` and `update`. These we will fill in with the game's code. * The `width` and `height` properties. You can set these to anything you like, I did not make it `window.innerWidth` and `window.innerHeight` because scaling can quickly become messy. It is easier to make it a fixed width for everybody. So now if you run your repl you should see a black square in your browser window. Success! If you do not, make sure you have the Phaser script in the right place and that you have the code in your `script.js` exactly like above. You should also get a message in the console, something like: ```%c %c %c %c %c Phaser v3.14.0 (WebGL | Web Audio) %c https://phaser.io background: #ff0000 background: #ffff00 background: #00ff00 background: #00ffff color: #ffffff; background: #000000 background: #fff``` This may look awful in the repl.it console but if you open it in a new tab and check the console it should be a colorful banner. ### Loading Assets The `preload` function that we are going to use for this section is where you load your assets. If you want some images or audio (Phaser does that as well) in your game you first have to load it here. This way you are loading all the required assets immediately and you can use them throughout the game. I have made some assets for this tutorial so that you do not need to find or make some yourself. Go [here](https://drive.google.com/drive/folders/1TzRicUBL8V0T_9fPMaNCL6M0UEV51irQ?usp=sharing) and click download like so to get the files: ![image](https://storage.googleapis.com/replit/images/1539468612344_eeb16f0a94e74fa401749d11f7b89333.pn) If you get the files in a `.zip` folder just unzip them and drop them into your repl. Once you have them in your repl we have to load them into our game. The following code in the `preload` function will do the trick: ```javascript this.load.atlas("player", "spritesheet.png", "sprites.json"); this.load.image("platform", "platform.png"); this.load.image("spike", "spike.png"); this.load.image("coin", "coin.png"); ``` The first parameter in all of the functions is the "key" for the image. This key you would use when you need to add the image into the game. You can put it as whatever you want but make sure it is descriptive of the image in some way. I suggest you keep them the same as mine so that later code in my tutorial works for you. The second parameter is the path to the image. Because I put the assets in the same folder as the html and js files the path is just the name of the image. If you put your assets in another folder the file path string would look like `"folder_name/file_name.png"`. You may also have noticed that the first command is a bit different. It loads an __atlas__ and not an image. An atlas is a collection of images put together to make a larger image accompanied by a file that states where all the smaller images are. If you open the file `sprites.json` in the assets I gave you you should see that it contains a bunch of named objects that have x, y, width and height properties among others. Each object is an image inside the larger image. In this tutorial we will use the atlas for the player animations. All of the frames for the player (in our case only three) are in the `spritesheet.png` file. The third parameter for the atlas is the path to the `.json` file which we looked at already. If you now run the current code the screen should remain black and no errors should be in the console. If you see a web audio warning that is fine, it does not mean anything important. It's just chrome messing with you. ### Adding Objects to Our Game The `create` function is where the building of our game happens. It is run right after `preload` and is run only once. If you want to add an object to the game, this is where you do it. If you want to repeatedly create some object. Make a function (read below) that creates the object and run that as may times as you like. So we now have loaded some images but we need to have something happen on the screen. Let's add a function in the `create` function that will spawn our player in. Add this code to the `create`function: ```javascript this.spawnPlayer = ()=>{ this.player = this.physics.add.sprite(400, 250, "player", "sprite_0"); } this.spawnPlayer(); ``` I put this in a seperate function so that we can spawn the player multiple times. We are saving the player to __`this`__ which is the Phaser game object so that we can access it from anywhere. The function itself creates a sprite (image/object) that is in the Phaser physics system. The parameters are: 1. X position 2. Y position 3. Image key 4. (optional) If the image is an atlas, which frame in the atlas. There may be a few more parameters but those are not important for this tutorial. The way we find out which frame is which in the atlas is by looking at the `sprites.json` file. Find an object and look at its x and y properties. For example `sprite_2` has the following object: ```javascript "sprite_2":{"frame":{"x":0,"y":0,"w":48,"h":64}... ``` We can see that the x and y coordinates of the frame are `0, 0`. This means that it will be in the top left corner. If you look at the top left corner of the `spritesheet.png` image you will see which frame is `sprite_2`. Try changing the last parameter in the add function to be `sprite_2`. You will see that it has changed. ##### Adding a Background If the only background color we could have would be black Phaser would look really bad. Luckily enough, Phaser has an easy way to add a background to our game. Just add this code to the top of your `create` function above the `spawnPlayer` function: ```javascript this.cameras.main.setBackgroundColor('#ffffff'); ``` This sets the background color for our main camera to white. If you have not used hex color codes before don't worry about it, just know that `#ffffff` is white. The only problem with that is that now we can't see where our canvas windw starts and ends. We can fix this with a little CSS: ```css canvas{ border: 1px solid black; } ``` Now if you run your code it should look something like this: ![image](https://storage.googleapis.com/replit/images/1539468654218_bf2de4d9dc55069fde105a14f6c9818a.pn) You can see we have our little character in the middle of the screen. The background is now white. You may have noticed that the character is not offset to a side even though we put in the coordinates for the center of the screen. This is because Phaser draws images from their center. This makes it easier to center images. Another simple thing we can add to the game is a camera that follows the player. This is quite easy to do in Phaser: ```javascript this.spawnPlayer = ()=>{ this.player = this.physics.add.sprite(400, 250, "player", "sprite_0"); this.cameras.main.startFollow(this.player); }; this.spawnPlayer(); ``` The function should be quite self-explanatory and if you run it you should see no change for now. As long as you do not get any errors you are fine. ### Adding Platforms Before we start I wanted to show you the most basic way to add an image to the game. The method used above has a very specific use case (only for sprites). Here is a more general use way of doing it: ```javascript // This goes beneath the spawnPlayer function call this.platform = this.add.image(404, 302, "platform"); ``` This is good for some simple use cases like for example a logo image in your title screen. However it has its shortcomings. Imagine you want to create a map of these platforms. You would have to add `platform1` `platform2` and so on... It would be a nightmare. Let's not get started on collisions. So by now you can see why we are not going to use this to add our platforms. Instead we will have a group. Defining a new group is easy. Remove the above code and add this instead. ```javascript this.platforms = this.physics.add.staticGroup(); ``` Currently we are just defining a new static (non-moving) group and assigning it to the variable `this.platforms`. If you run this now the platform image will disappear. That is because we need to add some platforms to the group. This can be done simply like this: ```javascript //Below the spawnPlayer function this.platforms = this.physics.add.staticGroup(); this.platfroms.create(404, 302, "platform"); ``` There we go! Now we have our platform back! But what is the benefit? In a moment when we deal with collisions you will see why. For now we will leave the platforms and get back to them later. ### Keyboard Inputs As you have probably gathered by now, Phaser has made its own version of everything you may need when developing games. Keyboard inputs are no exception. Phaser even supports many ways to do keyboard inputs. We are going to do the shortest and simplest. We are going to have a bunch of variables, one for each key. And we will check each frame if any of the keys are pressed and set velocities accordingly. The code for the keyboard variables in the `create` function looks like this: ```javascript this.key_W = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.W); this.key_A = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.A); this.key_D = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.D); ``` You do not need to understand this, just get the idea of what is happening. When a player presses a key the variable associated with that key will have `isDown` set to `true`. This makes adding keybinds really easy. Now for the rest of this section we are using the `update` function. `update` is your game loop. This function is run very fast repeatedly all throughout your game. This is where you would handle things like movement and other stuff you would want to check every frame. If you would be coding your own physics this would be where you do it. In the `update` function now let's check if the W key is pressed: ```javascript if(this.key_W.isDown){ this.player.setVelocityY(-50); }else{ this.player.setVelocityY(0); } ``` Instead of incrementing or decrementing the Y property of the player we set its velocity. We do this because it sets a velocity within Phaser which has some benefits. First of all Phaser object velocities take into account the frame rate. If every frame you increase the position X of a player, the higher the frame rate the faster the player moves. However, Phaser counteracts this. We do not need to know how, just that no matter the frame rate the player will always move at the same speed. The value we put into `setVelocityY` is the amount of pixels we want it to move in one second. If you run this now you will see that is you press the W key your character will move up. Success! Now let's add keybinds for A and D. This is only a few more lines of code: ```javascript if(this.key_A.isDown){ this.player.setVelocityX(-50); }else if(this.key_D.isDown){ this.player.setVelocityX(50); }else{ this.player.setVelocityX(0); } ``` We have this in an if/else if statement because we don't want to head left and right at the same time. We can only go in one direction or the other. And that's it! We now have linked up our keyboard keys to our Phaser game! Now it's time to deal with physics. ### Game Physics Phaser also has its own physics engine. In fact it has three but we will only use the most basic one for this tutorial. Just simple square and square collisions. Before we can do collisions, how about we add some gravity. We only need it on the player so it would look like this: ```javascript this.spawnPlayer = ()=>{ this.player = this.physics.add.sprite(400, 250, "player", "sprite_0"); this.player.body.setGravityY(800); this.cameras.main.startFollow(this.player); }; ``` Now if you run your game you will see that the player drops. But he is dropping very slowly. Why so? This is because each frame we are setting his velocity to 0 if the W key is not pressed. Previously that was needed so that he would not just fly away but now we need to remove that bit: ```javascript //In the update function if(this.key_W.isDown){ this.player.setVelocityY(-50); }/*else{ this.player.setVelocityY(0); } NO LONGER NEEDED*/ ``` Now if you run it the player falls a bit faster. You can still fly with W but we will change that in a second. #### Collisions Now that we have gotten gravity mostly out of the way let's make the player collide with the platform that we have. We can do this with one simple line of code. Add this to your `spawnPlayer` function: ```javascript this.physics.add.collider(this.player, this.platforms); ``` That's it. Just one line of code does everything. But if you run this now it will not work. The player will fall right through. And this is actually for a really stupid reason. We are running this code before we add the platforms. All you have to do is move the `spawnPlayer` function __call__ (not the function itself) below where we add the platforms. And Viola! We have the player not falling through the platform. There are some small problems that we should address before moving on. First of all, When we press W we can fly endlessly. That defeats the point of the game. To prevent this all we need to do is to only let us jump when we are on the ground. This is easy to do: ```javascript if(this.key_W.isDown && this.player.body.touching.down)... ``` When the key W is down and the player's body is touching a platform with its bottom it will jump. If you run this now you will see that the player now makes many little jumps if you press W. To make the jumps larger we have to increase the `setVelocitY`: ```javascript if(this.key_W.isDown && this.player.body.touching.down){ this.player.setVelocityY(-550); } ``` And also while we are at it we can make the left/right movement a bit faster: ```javascript if(this.key_A.isDown){ this.player.setVelocityX(-150); }else if(this.key_D.isDown){ this.player.setVelocityX(150); }else{ this.player.setVelocityX(0); } ``` So there we have it! A running and jumping player! Now let's give him a map to run around in. #### Map Building Phaser supports multiple ways to build a map (of course). However, I have decided that it would be better to cook up our own map builder that would work off of a string. Spaces would indicate that at that position there is no platform, 1 would mean that there is, 2 that this is a spawn point for the player and a dot(.) would mean that this is the end of a row. The map I designed looks something like this: ```javascript //At the top of your js file const map = '11111111111111111111111111.'+ '1 1.'+ '1 1.'+ '1 2 1 1 1 1 1.'+ '1 1 1 1 1 1.'+ '1 1.'+ '1 1.'+ '1 1 1 1 1 1.'+ '1 1 1 1 1 1.'+ '1 1.'+ '1 1.'+ '1 1 1 1 1 1.'+ '1 1 1 1 1 1.'+ '1 1.'+ '1 1.'+ '11111111111111111111111111'; ``` You can see that it is a box that is riddled with platforms. How do we turn this into a map? The parser for this that I made is only a few lines of code: ```javascript //Goes instead of the previous platform adding code this.platforms = this.physics.add.staticGroup(); let mapArr = map.split('.'); let drawX = 0; let drawY = 0; mapArr.forEach(row=>{ drawX = 0; for(let i = 0; i<row.length; i++){ if(row.charAt(i)==='1'){ this.platforms.create(drawX, drawY, "platform"); }else if(row.charAt(i)==='2'){ if(row.charAt(i+1)==='1'){ this.spawnPlayer(drawX-4, drawY-12); }else if(row.charAt(i-1)==='1'){ this.spawnPlayer(drawX+4, drawY-12); }else{ this.spawnPlayer(drawX, drawY-12); } } drawX+=40; } drawY+=40; }); ``` First we split the string that we have into an array of rows using the . that says that a row ends at that point. Then we loop through each row and at each row we loop through each character in the row. If the character is a 1, we add a platform at that place. If the character is 2 we spawn the player. I have a bit more code there that checks if there is a platform to the left or right and that nudges the character to a side just so that the player does not spawn in a platform. Also, you may have noticed that we are calling `spawnPlayer` here with some parameters. These are just x and y coordinates of where to spawn. To make that work we just have to edit the `spawnPlayer` function like so: ```javascript this.spawnPlayer = (x, y)=>{ this.player = this.physics.add.sprite(x, y, "player", "sprite_0"); this.player.body.setGravityY(800); this.cameras.main.startFollow(this.player); }; ``` Now if you run this you should get a map inside of which the player can run around. You can mess around with the map string if you want and design your own map. I would love to see what you come up with in the comments! ### Player Animations A while ago, I mentioned that we would use the atlas for player animations. Now is the time! We have three frames in our atlas and we have only used one. It's time to use the other two. Phaser has its own animation manager (by now you get the idea - Phaser === everything) that makes it super simple to do animations. First we have to set up our animations: ```javascript // At the bottom of the create function this.anims.create({ key:"walk", frames:[{key:"player", frame:"sprite_2"}, {key:"player", frame:"sprite_1"}], frameRate:10, repeat:-1 }); this.anims.create({ key:"stand", frames:[{key:"player", frame:"sprite_0"}], frameRate:1 }); ``` This creates an animation for our player that we can play when we want. The array `frames` is what Phaser will loop though and play. `frameRate` is quite self explanatory - the amount of frames that are played each second. `repeat` with the value -1 will make the animation loop again and again. Not specifying `repeat` will just make it run once. The key is the string that we can use to reference to the animation later. Just the same way as with images. Now let's run the animations when we walk right or left: ```javascript //In the update function if(this.key_A.isDown){ this.player.setVelocityX(-200); this.player.anims.play("walk", true); }else if(this.key_D.isDown){ this.player.setVelocityX(200); this.player.anims.play("walk", true); }else{ this.player.anims.play("stand", true); this.player.setVelocityX(0); } ``` The `true` parameter is just whether if there is already an animation running, should Phaser continue it? If you set this to false you will see that it will just freeze on a frame. That is because every frame it is checking if a key is pressed and then playing the animation. It will start again every frame making it look like it is frozen. Now if you run this you will see that we have a running animation with the legs moving and the hat bobbing up and down. There is only one more problem with the sprite. The player does not flip when he runs to the left. This is an easy fix: ```javascript //In the update function if(this.key_A.isDown){ this.player.setVelocityX(-200); this.player.anims.play("walk", true); this.player.flipX = true; }else if(this.key_D.isDown){ this.player.setVelocityX(200); this.player.anims.play("walk", true); this.player.flipX = false; }else{ this.player.anims.play("stand", true); this.player.setVelocityX(0); } ``` There we go! Now we have a map, player animations, keybinds, physics and most of all - a weird blob of a character who has a hat that flaps in the breeze! ### The Final Step - Spikes and Coins Now let's add some spikes that the player has to dodge and some coins that the player can collect. First, let's add a score counter in the top of the screen that displays our score: ```javascript this.spawnPlayer = (x, y)=>{ this.player = this.physics.add.sprite(x, y, "player", "sprite_0"); this.player.body.setGravityY(800); this.physics.add.collider(this.player, this.platforms); this.cameras.main.startFollow(this.player); //====================================== this.player.score = 0; this.scoreText = this.add.text(0, 0, "Score: "+this.player.score, { fill:"#000000", fontSize:"20px", fontFamily:"Arial Black" }).setScrollFactor(0).setDepth(200); }; ``` `setScrollFactor(0)` will make sure that when our camera moves, the text does not. This way it will always be in the same position in the top-left of the screen. Text is drawn from its top-left (don't ask me why it is one way for one thing and another for another) so drawing it at `0, 0` will put in the top-left corner. `setDepth(200)` will make sure the text always appears on top. We also make a variable for the score of the player that can be increased when we collect a coin. #### Coins Time to make an incentive to run and jump around. Coins will be a `c` in our map string. So, the map would now look like this: ```javascript const map = '11111111111111111111111111.'+ '1 c 1.'+ '1 c c c 1.'+ '1 2 1 1 c 1 c 1 1.'+ '1 1 1 1 1 1.'+ '1 1.'+ '1 c c 1.'+ '1 c 1 1 c 1 c 1 1.'+ '1 1 1 1 1 1.'+ '1 1.'+ '1 c c c 1.'+ '1 1 c 1 c 1 1 1.'+ '1 1 1 1 1 1.'+ '1 1.'+ '1 c c c c 1.'+ '11111111111111111111111111'; ``` Now to make this work we have to add an option of what to do if the current character is a `c` in our map parser. I added something like this: ```javascript this.platforms = this.physics.add.staticGroup(); //================================== this.coins = this.physics.add.group(); //================================= let mapArr = map.split('.'); let drawX = 0; let drawY = 0; mapArr.forEach(row=>{ drawX = 0; for(let i = 0; i<row.length; i++){ if(row.charAt(i)==='1'){ this.platforms.create(drawX, drawY, "platform"); }else if(row.charAt(i)==='2'){ if(row.charAt(i+1)==='1'){ this.spawnPlayer(drawX-4, drawY-12); }else if(row.charAt(i-1)==='1'){ this.spawnPlayer(drawX+4, drawY-12); }else{ this.spawnPlayer(drawX, drawY-12); } //================================= }else if(row.charAt(i)==='c'){ this.coins.create(drawX, drawY+10, "coin"); } //================================= drawX+=40; } drawY+=40; }); ``` If you run this you will see that a bunch of little coins appear. But we can't collect them! This is fairly easy to add: ```javascript // Add this after the map parsing code this.physics.add.overlap(this.player, this.coins, this.collectCoin, null, this); ``` This function will check if there is an overlap between two objects. The two objects are the first two parameters. If there is an overlap, it will run the function that is passed in with the third parameter. `null` is just there for reasons and `this` is just passing on the `this` value to the function. We now need to make a function `collectCoin` that will run if there is an overlap: ```javascript this.collectCoin = (player, coin)=>{ player.score+=10; this.scoreText.setText("Score: "+ this.player.score); coin.destroy(); }; ``` If you run this you will see that you can now collect coins and increase your score. Success! There is only one more step before we are done. #### Spikes Time to add some difficulty to the game. We are going to have spikes that if you step on they will clear your score and respawn you. Let's first add them to our map as an `s`: ```javascript const map = '11111111111111111111111111.'+ '1 c 1.'+ '1 c c s c 1.'+ '1 2 1 s 1 c 1 c 1 1.'+ '1 1 1 1 1 1.'+ '1 1.'+ '1 c c s s 1.'+ '1 c 1 s 1 c 1 c 1 1.'+ '1 1 1 1 1 1.'+ '1 1.'+ '1 c s c c 1.'+ '1 s 1 c 1 c 1 s 1 1.'+ '1 1 1 1 1 1.'+ '1 1.'+ '1 c c c c 1.'+ '11111111111111111111111111'; ``` And now we can render them into our game: ```javascript this.platforms = this.physics.add.staticGroup(); this.coins = this.physics.add.group(); //================================== this.spikes = this.physics.add.group(); //================================== let mapArr = map.split('.'); let drawX = 0; let drawY = 0; mapArr.forEach(row=>{ drawX = 0; for(let i = 0; i<row.length; i++){ if(row.charAt(i)==='1'){ this.platforms.create(drawX, drawY, "platform"); }else if(row.charAt(i)==='2'){ if(row.charAt(i+1)==='1'){ this.spawnPlayer(drawX-4, drawY-12); }else if(row.charAt(i-1)==='1'){ this.spawnPlayer(drawX+4, drawY-12); }else{ this.spawnPlayer(drawX, drawY-12); } }else if(row.charAt(i)==='c'){ this.coins.create(drawX, drawY+10, "coin"); //================================== }else if(row.charAt(i)==='s'){ this.spikes.create(drawX, drawY+10, "spike"); } //================================== drawX+=40; } drawY+=40; }); ``` Let's do what we did last time - add an overlap detector between the player and the spikes. The code is pretty much the same: ```javascript //Next to the other overlap checker for the coins this.physics.add.overlap(this.player, this.spikes, this.die, null, this); ``` And now we have to make a function `die` that will be run when the player hits the spike. All we will do is stop the game and display text saying **YOU DIED**: ```javascript this.die = ()=>{ this.physics.pause(); let deathText = this.add.text(0, 0, "YOU DIED", { color:"#d53636", fontFamily:"Arial Black", fontSize:"50px" }).setScrollFactor(0); Phaser.Display.Align.In.Center(deathText, this.add.zone(400, 250, 800, 500)); } ``` `this.physics.pause` is what stops the game. The text adding should be pretty self explanatory. The bit that may be confusing is the line after that. This is the code I used to center the text. It accepts two arguments - the object to center and the zone in which to center it in. `this.add.zone` in turn accepts four arguments - the x, y, width and height of the zone. The x and y are in the center of the screen and the width is the width of the screen and the same for the height. When you run this code and jump on a spike you will see that it shows some big red text saying __YOU DIED__. And there we have it! Our completed game! Make sure to celebrate by wrapping __lots__ of duct tape around some cardboard. That was what the duct tape and cardboard were for. Nothing, really :). ## Final Word Thank you for sticking to the end of this monster of a tutorial. I hope you are proud of what you have made. If you liked this tutorial, please show support by voting for it. If you have any questions, suggestions or if you found a typo don't hesitate to post it in the comments! Also, if you put a spin on the game or make a cool map that be sure to share it! I would love to see what you guys can make out of this :). If you are too lazy to scroll up, the link to the repl that I made is [here](https://phaser-tutorial.kaldisberzins.repl.co/). Also, if you would like me to make some follow up tutorials outside of the competition about some more advanced features like scenes and (multiplayer?) then be sure to leave a comment. If enough people want it I will be sure to make some more tutorials. ## __EDIT__ I made an additional demo that delves into some more complicated concepts but it looks a lot better (I stole some sprites off the internet). It is just some uncommented code that you can play around with and try and see if you can make anything out of it. If you want to check it out you can find it [here](https://repl.it/@kaldisberzins/Phaser-Demo). Just wanna play it? Go [here](https://phaser-demo--kaldisberzins.repl.co/). Also, thank you guys for all the support in the comments! So heartwarming to see that many people like it. [email protected]_
18
posted by kaldisberzins (209) 5 months ago
โ–ฒ
11
Hosting discord.py bots with repl.it
# Hosting [discord.py](http://discordpy.readthedocs.io/en/latest/) bots on [repl.it](https://repl.it/) *This tutorial shows you how you can host your **discord.py** bots on [repl.it](https://repl.it/).* *Before you start, you should already have a working bot. Also, this really doesn't matter if you're using [discord.py async](https://discordpy.readthedocs.io/en/async/) or [discord.py rewrite](https://discordpy.readthedocs.io/en/rewrite/). Works like a charm for both!* *Just to ease things, we'll be using the end product of [this tutorial](https://repl.it/talk/learn/Discordpy-Rewrite-Tutorial-using-commands-extension/10690).* ------ ### What we'll be doing? 1. *Creating a web server.* 2. *Using [uptimerobot](https://uptimerobot.com) to ping our bot every 30 minutes.* *We do this because our repl goes offline after 1 hour of inactivity. So we keep pinging the repl every 30 minutes, to avoid it being idle for one hour.* # Step 1: The requirements.txt file. Once you have your bot ready for hosting create a new file named `requirements.txt` in the root directory of your [python repl](https://repl.it/languages/python3). Open it up, the `requirements.txt` file holds all your project dependencies. Make sure to add the appropriate package names for the packages you're already using. A sample file for basic bots are given below: - ------ ``` git+https://github.com/Rapptz/[email protected]#egg=discord.py[voice] ``` `requirements.txt` for a discord.py rewrite bot. ------ ``` discord.py ``` `requirements.txt` for a discord.py async bot. ------ Now that you have the basic requirements ready, add one more requirement i.e. `flask`. Making your `requirements.txt` something like: - ``` git+https://github.com/Rapptz/[email protected]#egg=discord.py[voice] flask ``` **Note :** *I am sticking to discord.py rewrite bot, but it really doesn't matter again.* ------ [flask](http://flask.pocoo.org/) is a python module for handling [http requests](https://www.tutorialspoint.com/http/http_requests.htm). ------ # Step 2: Setting up a server Setting up a server using Flask is very easy. But to keep things simple and your bot's file clean, we'll make a server in a new file and name it `keep_alive.py` since that is what most people like to call the file that helps keep their bot alive. Open up the `keep_alive.py` file and import `Flask` also import `Thread` you can learn more about Threads [here](https://docs.python.org/3/library/threading.html). ```python from flask import Flask from threading import Thread ``` We didn't have to add threading to our requirements since it comes with python. Next, create the actual server. ```python app = Flask('') ``` Easy, isn't it? But it's not over just yet. Next, we add a route. A route is basically what handles http requests. Without a route the existence of our server does nothing. So add the following code ```python @app.route('/') def main(): return "Your bot is alive!" ``` `@app.route()` is a decorator which defines a route. `'/'` is the default route or the homepage of our server. The `main` function is called whenever a user makes a request to the `'/'` route which basically returns whatever has to displayed on the webpage, here we simply send the string `Your bot is alive` to keep things simple. Now that our server is ready, we need to make sure that it runs and http requests can be made to it. ```python def run(): app.run(host="0.0.0.0", port=8080) ``` You can basically put any 4 digit number in port which is not already in use. The defaults for python is `8080` and that's what we'll stick to. Finally, we make a function that we can call to prepare our server and allow it to be pinged. ```python def keep_alive(): server = Thread(target=run) server.start() ``` And we're basically done with our `keep_alive.py` file. ------ # Step 3: Combining the server and bot. Now that we're done making both the bot and the server, we need to combine the two. That is easy too, open up the `main.py` file ( or whatever file has the `discord_client.run(bot_token)` function in it ) and add the following line at the top: - ```python import keep_alive ``` This will import the keep_alive file in your bot's main file. Now just before starting the bot, at the bottom of the same file write the following code: - ```python keep_alive.keep_alive() ``` This will run the `keep_alive()` function that we defined in the `keep_alive.py` file earlier. And now your server is ready too! Run the bot, you should see something like this: - ![Keep Alive](https://i.imgur.com/XA3cvpK.png) Notice that small window saying `Your bot is alive!` if you see it, you successfully created the server! Only the last step remains! Before you go into the last step, make sure to copy the `repl.co` link you see in that small window's address bar. (`https://discordpy-rewrite.thedrone7.repl.co` in the picture, it will be referred to as the `server link` in the rest of the tutorial) And in case you don't see it, go through this tutorial again and make sure you didn't miss something, also if it retains, don't hesitate to comment and tell me. ------ # Step 4: Setting up [uptimerobot](https://uptimerobot.com/) First of all visit https://uptimerobot.com/ and register an account just like any other website. (It is free of course) Next, Once, you're logged in, 1. Click on the `New Monitor` button. ![Imgur](https://i.imgur.com/c8WCZab.png) 2. Next, choose the `HTTP(S)` Monitor Type. Give it a friendly name of your choice ( naming it after your bot is recommended ) Paste your `server link` in the `URL/IP` field. Set the Monitoring Interval to anything between 5 minutes to 45 minutes. ( 30 minutes is recommended) Optionally, Select yourself as one of the `"Alert Contacts To Notify"` so you are notified via e-mail whenever your bot goes offline. Finally making it look something like this: - ![Uptimerobot final monitor](https://i.imgur.com/yzRdE2K.png) 3. Click on `Create Monitor` and you're good to go! ------ Now you're bot will stay online for as long as it can. *( Repl it being just another host, won't be able to host it 24/7 so it **will** have a downtime of a few minutes within every 24 hours.)* And you're done! > Please upvote if you found this useful! > > Also, comment down below if there's something you would like to share or ask. And finally, thanks for reading it!
4
posted by TheDrone7 (101) 29 days ago
โ–ฒ
13
Making a discord bot using the discord.js Commando framework
# Discord.js Commando Framework tutorial *First of all, I recommend you all that you first go through the basics of JavaScript and Node.js from anywhere you'd like to though I personally recommend https://www.w3schools.com/ . Also, you should know how to create discord apps at https://discordapp.com/developers/applications/me and should've already invited the bot to your test server, this is only a tutorial for the commando framework, not the whole bot thing.* *Next, let's get started* - ### Step 1: Setting up the project 1. *Create a **new folder** (Make sure there are no spaces in your folder name) in your system and open **command prompt** or **power shell** in the folder.* 2. *Run the command* `npm init` *and fill out the form, If you don't know what you're doing, then leave everything to default and type in you name when it asks for* `author`. ```powershell PS E:\tutorial-bot> npm init This utility will walk you through creating a package.json file. It only covers the most common items, and tries to guess sensible defaults. See `npm help json` for definitive documentation on these fields and exactly what they do. Use `npm install <pkg>` afterwards to install a package and save it as a dependency in the package.json file. Press ^C at any time to quit. package name: (tutorial-bot) version: (1.0.0) description: A new discord bot entry point: (index.js) test command: git repository: keywords: author: HS license: (ISC) About to write to E:\tutorial-bot\package.json: { "name": "tutorial-bot", "version": "1.0.0", "description": "A new discord bot", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "HS", "license": "ISC" } Is this ok? (yes) PS E:\tutorial-bot> ``` 3. *After this, you will see a new file named* `package.json` *in your folder.* 4. *Now use the following command to install the required libraries* ```powershell PS E:\tutorial-bot> npm install --save discord.js discord.js-commando fs sqlite path ``` 5. *Now you'll see a new folder named* `node-modules` *in your bot's folder and you're ready to start coding the bot.* - ### Step 2 : Writing the index.js file 1. *Create a new file in your bot's folder called* `index.js` *and open it with your favourite text editor.* 2. *Now write the following code in the beginning of your **index.js** file.* ```javascript const commando = require('discord.js-commando') const path = require('path') const sqlite = require('sqlite') ``` *These lines will import the necessary libraries to use.* 3. *Next, write the following code* ```javascript const client = new commando.CommandoClient({ owner: '374886124126208000', // Your ID here. commandPrefix: '==', // The prefix of your bot. unknownCommandResponse: false, // Set this to true if you want to send a message when a user uses the prefix not followed by a command }) ``` *This will create a* `CommandoClient` *Object which is an extension of* `discord.js.Client`*class and set the owner of the bot as yourself to allow you to use the owner only commands that come built-in with Commando.* 4. *Now we will import the default commands for our bot.* ```javascript client.registry.registerDefaults() ``` 5. *Now let's make a handler that displays a message in the console when the bot is logged in and ready to use.* ```javascript client.on('ready',()=>{ console.log(`Logged in and ready to be used.. use "${client.commandPrefix}help".`) }) ``` 6. *Next, set up the settings provider, Commando comes with built-in sqlite settings provider to allow default commands like* `prefix` *work properly.* ```javascript client.setProvider( sqlite.open(path.join(__dirname, 'settings.sqlite3')).then(db => new Commando.SQLiteProvider(db)) ).catch(console.error); ``` 7. *Last of all, login the bot* ```javascript client.login("BOT_TOKEN") ``` - ### Testing the defaults 1. *Run the bot by using the command* `node index` *in your console.* 2. *After a while you will see a message similar to this: -* ```powershell PS E:\tutorial-bot> node index Logged in and ready to be used.. use "==help". ``` *You will also observe that your bot comes online so it's ready to be checked out.* 3. *Use the help command which in my case is* `==help` *It shows something like this in your DMs: -* ``` To run a command in *Server Name*, use == command or @Bot command. For example, == prefix or @Bot prefix. To run a command in this DM, simply use command with no prefix. Use help <command> to view detailed information about a specific command. Use help all to view a list of all commands, not just available ones. Available commands in *Server Name* Commands groups: Lists all command groups. enable: Enables a command or command group. disable: Disables a command or command group. reload: Reloads a command or command group. load: Loads a new command. unload: Unloads a command. Utility help: Displays a list of available commands, or detailed information for a specified command. prefix: Shows or sets the command prefix. ping: Checks the bot's ping to the Discord server. eval: Executes JavaScript code. ``` #### Details - **groups**: The commands in a commando bot are grouped together under categories. the `group` command can be used the list these categories. - **enable/disable**: Commando framework comes with an in-built feature of enabling/disabling commands in different guilds, these commands do the job. - **load/unload**: These commands can be used to register commands from outside the code. - **reload**: This command can be used to reload a command after you've made changes to the code to refresh it. - **help**: The in-built help function ( very detailed ). - **prefix**: Commando also comes with a feature to change the default prefix in different servers, the `prefix` command helps the users do it. - **ping**: Does exactly what help says. - **eval**: runs the code passed as an extension of the `index.js` file and returns the output ( recommended not to use ). ***These are the commands that come built-in with commando framework.*** - ### Creating your own commands 1. *First of all, create a folder named* `commands` *in your bot's root directory.* 2. *Next make the following change in your code* ```javascript client.registry.registerDefaults() ``` *to* ```javascript client.registry.registerDefaults() .registerGroups([ ['test', 'Starter Commands'] ]) .registerCommandsIn(path.join(__dirname,"commands")) ``` *This will create a new group named **Starter Commands** for your bot which can be referred to in the code by the name **test** and also read the files inside the **commands** folder and treat them as commands for the bot*. 3. *Inside the **commands** folder, create another folder named* `test` *and inside the **test** folder, create a file named* `foo.js` *as the first command will be a simple foo-bar command.* 4. *Open the* `foo.js` *file and change it's contents to* ```javascript const { Command } = require('discord.js-commando') class fooCommand extends Command{ constructor(client){ super( client, { name: 'foo', memberName: 'foo', aliases: ['f'], group: 'test', description: 'Responds with "bar".', details: 'The traditional foo-bar.', examples: ['==foo','==f'] }) } run(msg){ msg.say('bar') } } module.exports = fooCommand ``` ##### Details - *The first line imports the predefined* `Command` *class from the Commando Framework.* - *Next, we created the command as a extension of the* `Command` *class.* - *The constructor is run as soon as our **index.js** reads this file. Inside our constructor, we set the basic details about our command.* **name, memberName**: set the name of the command. **aliases**: sets what other commands can be used to do the same thing. **description, details**: describe what the command does. **examples**: sets an example for the user on how to use the command. **group**: sets our command will be part of which command group. - *The next part is the **run** method, it is called whenever the user uses the command, the parameter **msg** is the message sent by the user, it's of the type* `CommandoMessage`. *We make our bot respond with* `bar`*whenever the user uses the* `foo ` *command.* - *Finally,* `module.exports = fooCommand` *exports the command making it readable by our **index.js** file.* 5. *Now, let's test out our foo command.* *Run the bot using* `node index` *and use the **help** command.* *You will see something new at the end of the help menu* ``` Starter Commands foo: Responds with "bar". ``` *Let's use help about our command, use the **help foo** command. You should see something like: -* ``` __Command **foo:**__ Responds with "bar". **Format:** == `foo` or `@Bot foo` **Aliases:** f **Group:** Starter Commands (`test:foo`) **Details:** The traditional foo-bar. **Examples:** ==foo ==f ``` *Let's use our command now, there are 4 ways to do it now.* - Use `==foo` or `==f` in a server - Use `foo` or `f` in DMs *You will see that the bot responds with* `bar` *wherever you use the command.* ------ 6. *Now that we are done with one command, let's make another one that takes in input (**arguments**) from the user.* 7. *Create a new file named* `say.js` *in your **test** folder.* 8. *Write the following code in the **say.js** file.* ```javascript const { Command } = require('discord.js-commando') class sayCommand extends Command { constructor(client) { super(client, { name: 'say', memberName: 'say', group: 'test', aliases: ['echo', 'repeat'], description: 'A command that repeats whatever you say.', details: "Make me repeat your wordsmaking it look like I'm a parrot", examples: ['==say Hello World', '==repeat Who Am I?'], clientPermissions: ["MANAGE_MESSAGES"], args: [{ key: 'text', prompt: 'What do you wish for me to say?', type: 'string' }] }) } run(msg, { text }) { msg.say(text) return msg.delete() } } module.exports = sayCommand ``` ##### Let's see what's new! - **client_permissions**: defines a list of permissions that your not will need for the command to work properly. - **args**: Defines what arguments the user has to enter. - **key**: defines the name of the argument. - **prompt**: defines what the bot will say if the user has not provided the argument - **type**: defines the type of the argument. - *The run method now has 2 parameters, the second one is the arguments, we are asking the user to pass.* - `msg.delete()`*deletes the message sent by the user.* 9. *Let's test out our say command* *If you use the **help** command now, you will see a new entry at the bottom for the **say** command.* *Use **help say** before using the command.* *You should see something similar to: -* ``` __Command **say:**__ A command that repeats whatever you say. **Format:** == `say <text>` or `@Bot say <text>` **Aliases:** echo, repeat **Group:** Starter Commands (`test:say`) **Details:** Make me repeat your words making it look like I'm a parrot **Examples:** ==say Hello World ==repeat Who Am I? ``` *Notice how **Commando** prepared the **format** for how to use the command, trust me it's a handy feature.* *Use the* `say` *command now without passing any arguments.* *You will see that the bot asks you for the text to say, also with a message like* ``` Respond with `cancel` to cancel the command. The command will automatically be cancelled in 30 seconds. ``` *This is another feature of Commando framework - command cancellation.* *You can either say something for the bot to repeat it or wait for 30 seconds to cancel the command or say cancel yourself.* *You can also pass on the argument while using the command as in the examples.* - ### Validating and throttling *Now let's think, what if the user was trying to send links or spam in the server using our say command?* *That can easily be prevented by validating the* `say` *command and making sure that there was no link in the argument passed.* *As for the spam, it can be prevented by throttling i.e. making sure that the command is not used more than a specified number of times in a specified amount of time.* *Here's a sample code (This is modified* `say.js` *file) : -* ```javascript const { Command } = require('discord.js-commando') class sayCommand extends Command { constructor(client) { super(client, { name: 'say', memberName: 'say', group: 'test', aliases: ['echo', 'repeat'], description: 'A command that repeats whatever you say.', details: "Make me repeat your wordsmaking it look like I'm a parrot", examples: ['==say Hello World', '==repeat Who Am I?'], clientPermissions: ["MANAGE_MESSAGES"], args: [{ key: 'text', prompt: 'What do you wish for me to say?', type: 'string', validate: text=>{ if(text.indexof("http://") > -1 || text.indexOf("https://") > -1) return "You are trying to send a link." // Message to display when invalid argument has been passed should be returned else return true // true should be returned when the argument is valid } }], throttling: { duration: 60, // This is the duration in seconds usages: 2 // Allowed number of usages in the given duration } }) } run(msg, { text }) { msg.say(text) return msg.delete() } } module.exports = sayCommand ``` ##### Now let's see: - - **argument.validate: ** a function that checks for validation of the argument passed. Returns `true` when valid and the `error message` when invalid. - **throttiling:** an object that specifies how many usages in how much duration of time are allowed. - ### Messing with the defaults *You can also modify the default commands: -* *You can find them at* ``` YourBotDirectory/node_modules/discord.js-commando/src/commands/ ``` *In there, you will see 2 folders containing the command files.* - `/util/` - `eval.js` - enables the owner to run javascript code from discord. - `help.js` - shows help for the various commands. - `ping.js` - displays the bot's ping to the discord server. - `prefix.js` - changes the prefix for the server ( guild Only command ). - `/commands/` - `enable.js` - enables a command in a server. - `disable.js` - disables a command in a server. - `groups.js` - lists all command groups. - `load.js` - loads a new command. - `reload.js` - reload a command. - `unload.js` - unloads a command. ------ **That's all you need to know to get started, put your imagination to the test and make amazing bots. Here's the link to the official docs for more reference: **https://discord.js.org/#/docs/commando/master/general/welcome **Also here's a link to a Commando bot by @kpostal10 named Bug Bot just for reference: **https://repl.it/@kpostal10/BugBot
19
posted by TheDrone7 (101) 2 months ago
โ–ฒ
10
๐Ÿš€ Creating a VueJS app: A tutorial for beginners ๐Ÿ‘
# ๐Ÿš€ Creating VueJS app: Tutorial for beginners ๐Ÿ‘ [VueJS](https://vuejs.org) is one of the easier JavaScript frameworks for beginners to get into. It is a full featured JS framework that has the most stars of any JS framework on GitHub. I'm going to be explaining how to use the basics of this framework. I'll overexplain some parts, specifically the parts that tripped me up when I first started getting into VueJS and web dev. ### Prerequisites - Intermediate experience with HTML and CSS - Beginner / Intermediate experience with JavaScript # Getting started ## Boilerplate After, creating a new `HTML, CSS, JS` [repl](https://repl.it/repls), fill in the standard HTML boilerplate. ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>My VueJS App</title> </head> <body> </body> </html> ``` Next, add a link to the VueJS code from the CDN. Make sure you also create an `index.js` file and include it in your HTML. ```html <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script src="index.js"></script> ``` ## Let's use some Vue! ๐ŸŽ‰ In your HTML, all you have to do is add a `div` inside of your `body` tag with the `id` of 'app'. You're teling VueJS that your Vue app is going to be inside of `<div id="app"></div>`, and telling it to render the data called `myData`. ```html <body> <div id="app">{{ myData }}</div> </body> ``` So, you're whole index.html file is going to be looking like this: ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>My VueJS App</title> </head> <body> <div id="app">{{ myData }}</div> </body> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script src="index.js"></script> </html> ``` Now, all we have to do is create the JS part so it makes more sense. Inside the `index.js`, create a new Vue instance, passing an object as a parameter. Store that instance in the variable called `app`. ```js let app = new Vue({}); ``` However, that won't work just yet. We need to tell Vue more information: ```js let app = new Vue ({ el: '#app', data: { myData: 'Super cool VueJS application ๐Ÿ––' } }) ``` Boom! That is all your JS. Now Vue knows to render our Vue app in the HTML `el`ement that has the `id` of "app". Inside of the object `data`, we're declaring a property called `myData`, which is a string of text. This yields the following: ![image](https://storage.googleapis.com/replit/images/1551320145285_7902b5fac4930095a9cc332a4ff500a8.pn) ## Components Breaking up our application into smaller components will help make our application more maintainable. It's good practice to do this whenever it makes sense. For starters, we're going to be making a button component, shown below. ![Gif of clicking on an HTML button. The button includes the number of times it has been pressed. After each mouse button press, the number increases by one.](https://i.imgur.com/k4rXFkW.gif) To create a component, just use the `component` function and pass in a few parameters. ```js Vue.component('button-counter', { data: function () { return { count: 0 } }, template: '<button v-on:click="count++">{{ count }} ๐Ÿ‘ claps</button>' }) ``` This may be a lot, so I'll break it down. The first parameter is a string `my-counter`. We'll use it later when creating the component in the HTML. The second parameter is an object with properties `data` and `template`. The first property is `data`. It is a function that returns an object. This object contains the data of the component, in this case, `count`. ```js Vue.component('button-counter', { data: function () { return { count: 0 } } }) ``` If it's a bit hard to understand what's going on, try reading the following code. Below, we explicitly create an object called `obj`, then return `obj`. ```js Vue.component('button-counter', { data: function () { let obj = { count: 0 } return obj } }) ``` The second property `template` is a string that you can think of HTML. We create a button. The button will include the variable `count`, with some other text. ```js Vue.component('button-counter', { data: function () { let obj = { count: 0 } return obj }, template: '<button v-on:click="count++">{{ count }} ๐Ÿ‘ claps</button>' }) ``` What is this `v-on:click="count++"`? It is special VueJS template syntax. On the left hand side of the equal sign, we have `v-on:click`. We are telling Vue to perform some action *on* some *click* event. Vue is performing some action when it detects a *click* event that has occured (the user clicks on the `button`). We can also use `v-on:dblclick`, or any other event. The right hand side of the equal sign tells Vue what to do when the user clicks on the `button`. In this case it increments the variable count. We can do other things like call functions, but that's outside the scope of this tutorial. So when a user clicks the button, `count` increases by 1. Now just put your button component in your HTML, just like you would with a `p` or `div` tag: ```html<body> <div id="app"> <p>{{ myData }}</p> <button-counter></button-counter> </div> </body> ``` Since `<button-container></button-container>` is reusable, we can add as many buttons as we want: ```html<body> <div id="app"> <p>{{ myData }}</p> <button-counter></button-counter> <button-counter></button-counter> <button-counter></button-counter> <button-counter></button-counter> <button-counter></button-counter> <button-counter></button-counter> <button-counter></button-counter> <button-counter></button-counter> <button-counter></button-counter> <button-counter></button-counter> </div> </body> ``` It will yield something like this: (with a bit of extra formatting) ![image](https://storage.googleapis.com/replit/images/1551328064026_49702d996ecc9b279cd629e0ee1c150c.pn) Check out the finished repl [here](https://repl.it/@eankeen/vuejs-tutorial). If you want a more succinct explanation, check out the official [website](https://vuejs.org) and [docs](https://vuejs.org/v2/guide)! https://i.imgur.com/k4rXFkW.gifv
0
posted by eankeen (48) 23 days ago
โ–ฒ
71
Building AI: Neural Networks for beginners ๐Ÿ‘พ
Teaching Machine to recognize Hand-written Numbers! I am excited to share some of my experience studying machine learning with you, guys! I'm not an expert but I'll try to explain it the way I see it myself. I'm going to try to give you some intuition about how Neural Networks work, omitting most of the math to make it more understandable but, for the most curious of you, I'll leave the links to complete explanations/courses in the end. ![Predicted class_ 4](https://storage.googleapis.com/replit/images/1541033154259_e80e9609e421c89077d1b92aa1d33f36.pn) In 29 mins, you'll be able to configure an algorithm that's going to recognize the written digits in python :) ## **๐Ÿง  What is a Neural Network?** Imagine Neural Network as an old wise wizard who knows everything and can predict your future by just looking at you. ![magicbox](https://storage.googleapis.com/replit/images/1541033176006_a6af80c5d6034944a58c1fc595834691.pn) It turns out that he manages to do so in a very non-magical way: 1. Before you visited him, he trained, carefully studied everything about many thousands of people who came to see him before you. 2. He now collects some data about what you look like (your apparent age, the website you found him at, etc). 3. He then compares it to the historical data he has about people that came to see him before. 4. Finally, he gives his best guess on what kind of person you are based on the similarities. ![nn](https://storage.googleapis.com/replit/images/1541033195545_03966c8b28c6a5a6e037593ca2d5fd2f.gi) In very general terms, it is the way many machine learning algorithms work. They are often used to predict things based on the history of similar situations: Amazon suggesting the product you might like to buy, or Gmail suggesting to finish the sentence for you, or a self-driving car learning to drive. ## **๐Ÿ“™ Part 1: Import libraries** Let's start! I have put together a class that is doing all the math behind our algorithm and I'd gladly explain how it works in another tutorial or you could go through my comments and try to figure it out yourself if you know some machine learning. **For now, create a file called `NN.py` and paste this code:** ```python import numpy as np from scipy.optimize import minimize class Neural_Network(object): def configureNN(self, inputSize, hiddenSize, outputSize, W1 = np.array([0]), W2 = np.array([0]), maxiter = 20, lambd = 0.1): #parameters self.inputSize = inputSize self.outputSize = outputSize self.hiddenSize = hiddenSize #initialize weights / random by default if(not W1.any()): self.W1 = np.random.randn( self.hiddenSize, self.inputSize + 1) # weight matrix from input to hidden layer else: self.W1 = W1 if (not W2.any()): self.W2 = np.random.randn( self.outputSize, self.hiddenSize + 1) # weight matrix from hidden to output layerself.W2 = W2 else: self.W2 = W2 # maximum number of iterations for optimization algorithm self.maxiter = maxiter # regularization penalty self.lambd = lambd def addBias(self, X): #adds a column of ones to the beginning of an array if (X.ndim == 1): return np.insert(X, 0, 1) return np.concatenate((np.ones((len(X), 1)), X), axis=1) def delBias(self, X): #deletes a column from the beginning of an array if (X.ndim == 1): return np.delete(X, 0) return np.delete(X, 0, 1) def unroll(self, X1, X2): #unrolls two matrices into one vector return np.concatenate((X1.reshape(X1.size), X2.reshape(X2.size))) def sigmoid(self, s): # activation function return 1 / (1 + np.exp(-s)) def sigmoidPrime(self, s): #derivative of sigmoid return s * (1 - s) def forward(self, X): #forward propagation through our network X = self.addBias(X) self.z = np.dot( X, self.W1.T) # dot product of X (input) and first set of 3x2 weights self.z2 = self.sigmoid(self.z) # activation function self.z2 = self.addBias(self.z2) self.z3 = np.dot( self.z2, self.W2.T) # dot product of hidden layer (z2) and second set of 3x1 weights o = self.sigmoid(self.z3) # final activation function return o def backward(self, X, y, o): # backward propgate through the network self.o_delta = o - y # error in output self.z2_error = self.o_delta.dot( self.W2 ) # z2 error: how much our hidden layer weights contributed to output error self.z2_delta = np.multiply(self.z2_error, self.sigmoidPrime( self.z2)) # applying derivative of sigmoid to z2 error self.z2_delta = self.delBias(self.z2_delta) self.W1_delta += np.dot( np.array([self.z2_delta]).T, np.array([self.addBias(X)])) # adjusting first set (input --> hidden) weights self.W2_delta += np.dot( np.array([self.o_delta]).T, np.array([self.z2])) # adjusting second set (hidden --> output) weights def cost(self, nn_params, X, y): #computing how well the function does. Less = better self.W1_delta = 0 self.W2_delta = 0 m = len(X) o = self.forward(X) J = -1/m * sum(sum(y * np.log(o) + (1 - y) * np.log(1 - o))); #cost function reg = (sum(sum(np.power(self.delBias(self.W1), 2))) + sum( sum(np.power(self.delBias(self.W2), 2)))) * (self.lambd/(2*m)); #regularization: more precise J = J + reg; for i in range(m): o = self.forward(X[i]) self.backward(X[i], y[i], o) self.W1_delta = (1/m) * self.W1_delta + (self.lambd/m) * np.concatenate( (np.zeros((len(self.W1),1)), self.delBias(self.W1)), axis=1) self.W2_delta = (1/m) * self.W2_delta + (self.lambd/m) * np.concatenate( (np.zeros((len(self.W2),1)), self.delBias(self.W2)), axis=1) grad = self.unroll(self.W1_delta, self.W2_delta) return J, grad def train(self, X, y): # using optimization algorithm to find best fit W1, W2 nn_params = self.unroll(self.W1, self.W2) results = minimize(self.cost, x0=nn_params, args=(X, y), options={'disp': True, 'maxiter':self.maxiter}, method="L-BFGS-B", jac=True) self.W1 = np.reshape(results["x"][:self.hiddenSize * (self.inputSize + 1)], (self.hiddenSize, self.inputSize + 1)) self.W2 = np.reshape(results["x"][self.hiddenSize * (self.inputSize + 1):], (self.outputSize, self.hiddenSize + 1)) def saveWeights(self): #sio.savemat('myWeights.mat', mdict={'W1': self.W1, 'W2' : self.W2}) np.savetxt('data/TrainedW1.in', self.W1, delimiter=',') np.savetxt('data/TrainedW2.in', self.W2, delimiter=',') def predict(self, X): o = self.forward(X) i = np.argmax(o) o = o * 0 o[i] = 1 return o def predictClass(self, X): #printing out the number of the class, starting from 1 print("Predicted class out of", self.outputSize,"classes based on trained weights: ") print("Input: \n" + str(X)) print("Class number: " + str(np.argmax( np.round(self.forward(X)) ) + 1)) def accuracy(self, X, y): #printing out the accuracy p = 0 m = len(X) for i in range(m): if (np.all(self.predict(X[i]) == y[i])): p += 1 print('Training Set Accuracy: {:.2f}%'.format(p * 100 / m)) ``` ## **๐Ÿ“Š Part 2: Understanding Data** Cool! Now, much like the wizard who had to study all the other people who visited him before you, we need some data to study too. Before using any optimization algorithms, all the data scientists first try to *understand* the data they want to analyze. **Download files `X.in` (stores info about what people looked like - question) and `y.in`(stores info about what kind of people they were - answer) from [here](https://www.dropbox.com/sh/b04b2xb5j3ncir3/AABlau8wnzWmuyekJ8iVlmPga?dl=0) and put them into folder `data` in your repl.** * X: We are given 5,000 examples of 20x20 pixel pictures of handwritten digits from 0 to 9 (classes 1-10). Each picture's numerical representation is a single vector, which together with all the other examples forms an array `X`. * Y: We also have an array `y`. Each column represents a corresponding example (one picture) from `X`. `y` has 10 rows for classes 1-10 and the value of only the correct class' row is one, the rest is zeros. It looks similar to this: ``` [0, 0, 0, 0, 0, 0, 0, 0, 0, 1] # represents digit 0 (class 10) [1, 0, 0, 0, 0, 0, 0, 0, 0, 0] # represents digit 1 (class 1) ...... [1, 0, 0, 0, 0, 0, 0, 0, 1, 0] # represents digit 9 (class 9) ``` Now, let's plot it! ![TrainingData](https://storage.googleapis.com/replit/images/1541033299328_0478599ab1b7884435cc3bf629b1edc1.pn) In the end, I'd want a function `displayData(displaySize, data, selected, title)`, where * `displaySize` - the numer of images shown in any one column or row of the figure, * `data` - our X array, * `selected` - an index (if displaying only one image) or vector of indices (if displaying multiple images) from X, * `title` - the title of the figure **Create a `plots` folder to save your plots to. Also, if you use repl, create some empty file in the folder so that it doesn't disappear.** **Create a `display.py` file and write the following code in there. Make sure to read the comments:** ```python import matplotlib.pyplot as plt # Displaying the data def displayData( displaySize, data, selected, title ): # setting up our plot fig=plt.figure(figsize=(8, 8)) fig.suptitle(title, fontsize=32) # configuring the number of images to display columns = displaySize rows = displaySize for i in range(columns*rows): # if we want to display multiple images, # then 'selected' is a vector. Check if it is here: if hasattr(selected, "__len__"): img = data[selected[i]] else: img = data[selected] img = img.reshape(20,20).transpose() fig.add_subplot(rows, columns, i+1) plt.imshow(img) # We could also use plt.show(), but repl # can't display it. So let's insted save it # into a file plt.savefig('plots/' + title) return None ``` Great, we are halfway there! ## **๐Ÿ’ช Part 3: Training Neural Network** Now, after we understand what our data looks like, it's time to train on it. Let's make that wizard study! It turns out that the results of the training process of the Neural Networks have to be stored in some values. These values are called *parameters* or *weights* of the Neural Network. If you were to start this project from scratch, your initial weights would be just some random numbers, however, it would take your computer forever to train to do such a complex task as recognizing digits. For this reason, I will provide you with the initial weights that are somewhat closer to the end result. **Download files `W1.in` and `W2.in` from [here](https://www.dropbox.com/sh/b04b2xb5j3ncir3/AABlau8wnzWmuyekJ8iVlmPga?dl=0) and put them into `data` folder.** We are now ready to write code to use our Neural Network library! ![training](https://storage.googleapis.com/replit/images/1541033327074_30bf734c3f1fa129d15be3285be6e453.gi) **Create a `train.py` file and write the following code in there. Make sure to read the comments:** ```python # This code trains the Neural Network. In the end, you end up # with best-fit parameters (weights W1 and W2) for the problem in folder 'data' # and can use them to predict in predict.py import numpy as np import display from NN import Neural_Network NN = Neural_Network() # Loading data X = np.loadtxt("data/X.in", comments="#", delimiter=",", unpack=False) y = np.loadtxt("data/y.in", comments="#", delimiter=",", unpack=False) W1 = np.loadtxt("data/W1.in", comments="#", delimiter=",", unpack=False) W2 = np.loadtxt("data/W2.in", comments="#", delimiter=",", unpack=False) # Display inputs sel = np.random.permutation(len(X)); sel = sel[0:100]; display.displayData(5, X, sel, 'TrainingData'); # Configuring settings of Neural Network: # # inputSize, hiddenSize, outputSize = number of elements # in input, hidden, and output layers # (optional) W1, W2 = random by default # (optional) maxiter = number of iterations you allow the # optimization algorithm. # By default, set to 20 # (optional) lambd = regularization penalty. By # default, set to 0.1 # NN.configureNN(400, 25, 10, W1 = W1, W2 = W2) # Training Neural Network on our data # This step takes 12 mins in Repl.it or 20 sec on your # computer NN.train(X, y) # Saving Weights in the file NN.saveWeights() # Checking the accuracy of Neural Network sel = np.random.permutation(5000)[1:1000] NN.accuracy(X[sel], y[sel]) ``` **Now, you have to run this code either from:** * **Repl.it** - but you would need to move code from `train.py` into `main.py`. Don't delete `train.py` just yet. It would also take approximately 12 minutes to compute. You can watch [this](https://www.youtube.com/watch?v=z-EtmaFJieY) Crash Course video while waiting :) * **Your own computer** - just run `train.py`, which takes 20 sec on my laptop to compute. If you need help installing python, watch [this](https://www.youtube.com/watch?v=LrMOrMb8-3s) tutorial. ![trained](https://storage.googleapis.com/replit/images/1541033350157_38c60efaabc74234ee72285f0e17048b.pn) ## **๐Ÿ”ฎ Part 4: Predicting!** By now, you are supposed to have your new weights (`TrainedW1.in`,`TrainedW2.in`) saved in `data` folder and the accuracy of your Neural Network should be over 90%. Let's now write a code to use the trained weights in order to predict the digits of any new image! ![giphy](https://storage.googleapis.com/replit/images/1541033387111_bcb40155cc2e8ef38749e91c207033d8.gi) **Create a `predict.py` file and write the following code in there. Make sure to read the comments:** ```python import numpy as np import display from NN import Neural_Network NN = Neural_Network() # Loading data X = np.loadtxt("data/X.in", comments="#", delimiter=",", unpack=False) y = np.loadtxt("data/y.in", comments="#", delimiter=",", unpack=False) trW1 = np.loadtxt("data/TrainedW1.in", comments="#", delimiter=",", unpack=False) trW2 = np.loadtxt("data/TrainedW2.in", comments="#", delimiter=",", unpack=False) # Configuring settings of Neural Network: NN.configureNN(400, 25, 10, W1 = trW1, W2 = trW2) # Predicting a class number of given input testNo = 3402; # any number between 0 and 4999 to test NN.predictClass(X[testNo]) # Display output display.displayData(1, X, testNo, 'Predicted class: ' + str(np.argmax(np.round(NN.forward(X[testNo]))) + 1) ) ``` **Change the value of `testNo` to any number between 0 and 4999. In order to get a digit (class) prediction on the corresponding example from array X, run the code from:** * **Repl.it** - but you would need to move code from `predict.py` into `main.py`. Don't delete `predict.py` just yet. * **Your own computer** - just run `predict.py`. Yay, you are officially a data scientist! You have successfully: 1. Analyzed the data 2. Implemented the training of your Neural Network 3. Developed a code to predict new testing examples ![congrats](https://storage.googleapis.com/replit/images/1541033425490_308fdfc015d494a8aeceae5564956f4b.gi) ## **๐Ÿš€ Acknowledgments** Hat tip to @shamdasani whose code I used as a template for Neural Network architecture and Andrew Ng from Stanford whose data I used. Plenty of things I told you are not completely correct because I rather tried to get you excited about the topic I am passionate about, not dump some math on you! If you guys seem to enjoy it, please follow through with studying machine learning because it is just an amazing experience. I encourage you to take [this free online course](https://www.coursera.org/learn/machine-learning) on it to learn the true way it works. Also, it's my first post here and I'd appreciate any feedback on it to get better. Keep me updated on your progress, ask any questions, and stay excited! โœจโœจโœจ
15
posted by ArtemLaptiev1 (70) 5 months ago
โ–ฒ
82
Build a WhatsApp bot in 30 minutes ๐Ÿ•
A few months ago, I'd started making chatbots on [Telegram](https://t.me) - I'd seen APIs for WhatsApp but they were unoffical and there was a chance for getting your number blocked ๐Ÿ“ฑ โŒ A while ago, I saw that [Twilio](https://twilio.com) had an official WhatsApp API. 30 minutes later, I made a [Wikipedia bot on WhatsApp](https://wikibot.4ty2.fun) ๐Ÿ‘‡ ![](https://wikibot.surge.sh/Untitled-b9da3f92-94c0-4f97-8afb-787110d8a9d3.png) This is a tutorial to help you make a something like this, your own chatbots on WhatsApp - these bots are immediately available to 2 billion users, and there are so many things possible ๐ŸŽ“ I can't wait to see what you make! Now, let's get started ๐Ÿƒโ€โ™‚๏ธ ## ๐Ÿ”‘ Accounts and Keys First, Sign up for [Twilio](https://www.twilio.com/try-twilio) - it's free and you won't need a credit card ๐Ÿ’ณ ![](https://wikibot.surge.sh/screely-1535885763017-fc654067-9557-4bf7-98b5-4337911ff4ba.png) Once you're done verifying your phone number, select Procuts > Programmable SMS and then continue to name your project. ![](https://wikibot.surge.sh/screely-1535885937977-c5a924ec-8cc3-4430-9345-9b5e1dc74ef3.png) Feel free to skip steps for adding teammates - you won't need that for now. You must now take note of some authentication keys you'll need for building the WhatsApp bot ๐Ÿ‘‡ ![](https://wikibot.surge.sh/screely-1535886250966-f68b6cfb-c104-4adf-80e7-4e3f9bd15b5b.png) The final step - setup your WhatsApp Sandbox [here](https://www.twilio.com/console/sms/whatsapp/sandbox) - choose any number, and join your sandbox following instructions on the page. ![](https://wikibot.surge.sh/screely-1535886798623-1dac1ba9-c362-4e49-87ab-7bbb6138e8c7.png) Aaaaaand you're done with credential setup! Don't worry, that was the toughest part of this tutorial ๐Ÿ˜› ## ๐Ÿš€ Getting Started So that we don't spend too much time on setup, I've created an environment (with repl.it!) you can use within your browser. Head over [here](https://repl.it/@jajoosam/wikibot-start), and wait for a couple of seconds to fork it. Next, open up `server.js` and put in your Account SID and Auth Token, on lines `7` and `8` ```javascript const accountSid ="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; //Account SID const authToken ="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; // Auth Token ``` You can see, this environment already has dependencies installed, and an `express` server set up. We still need to give Twilio a URL to send incoming messages to, though ๐Ÿ”— Let's go back to the [WhatsApp Sandbox](https://www.twilio.com/console/sms/whatsapp/sandbox), and put in a webhook URL for incoming messages. ![](https://wikibot.surge.sh/Untitled-3ed5263b-c6d8-492b-ba08-b4644ab502cf.png) This URL must be what you see on the preview panel of your [repl.it](http://repl.it) project + `/incoming` ![](https://wikibot.surge.sh/Untitled-1779b21f-9100-4942-b732-320dc48c5f76.png) We can now finally read messages that are sent to the bot. Add a simple `console.log()` in your webhook handler ๐Ÿ‘‡ ```javascript app.post('/incoming', (req, res) => { console.log(req.body) }); ``` When you send a message to your bot, you should be able to see something like this in your repl console ๐Ÿ‘จโ€๐Ÿ’ป ![](https://wikibot.surge.sh/Untitled-163eb09e-e6ab-4910-badb-d8aa0aa789f7.png) Building an echo bot would look something like this, using `twiml` to write a message ๐Ÿ‘‡ ```javascript app.post('/incoming', (req, res) => { const twiml = new MessagingResponse(); twiml.message(req.body.Body); res.writeHead(200, {'Content-Type': 'text/xml'}); res.end(twiml.toString()); }); ``` But, since we're actually trying to build a useful bot - let's use informative APIs! ## ๐ŸŒ Fetching Information DuckDuckGo has an amazing, free instant answer API. It takes in a query and returns back a summary from WikiPedia and more. A few examples ๐Ÿ‘‰ [WikiPedia](https://api.duckduckgo.com/?skip_disambig=1&format=json&pretty=1&q=WikiPedia), [Macbook Air](https://api.duckduckgo.com/?skip_disambig=1&format=json&pretty=1&q=MacBook%20Air), [Twilio](https://api.duckduckgo.com/?skip_disambig=1&format=json&pretty=1&q=Twilio) I spent some time creating a decent parser which usually returns information from this API. Try pasting this code in your [repl.it](http://repl.it) project, and your [console](https://dsh.re/f7477c) should have stuff about Trump in it ๐Ÿ˜› ```javascript var base = 'https://api.duckduckgo.com/?skip_disambig=1&format=json&pretty=1&q='; var query = 'Donald Trump'; request(base + query, function (error, response, body) { body = JSON.parse(body) if(body["Abstract"] == ""){ body["Abstract"]= body["RelatedTopics"][0]["Text"] } var msg = body["Heading"]+"\n\n"+body["Abstract"]; console.log(msg) }); ``` Pretty straight forward, right? ๐Ÿ˜„ ## ๐Ÿ› ๏ธ Putting it all together To make our actual bot, all we need to do is get the query from our request - which we can get as `req.body.Body` - and use `twmil` to send across the data we collected in `msg` ```javascript app.post('/incoming', (req, res) => { const twiml = new MessagingResponse(); var base = 'https://api.duckduckgo.com/?skip_disambig=1&format=json&pretty=1&q='; var query = req.body.Body; request(base + query, function (error, response, body) { body = JSON.parse(body) if(body["Abstract"] == ""){ body["Abstract"]= body["RelatedTopics"][0]["Text"] } var msg = twiml.message(body["Heading"]+"\n\n"+body["Abstract"]); res.writeHead(200, {'Content-Type': 'text/xml'}); res.end(twiml.toString()); }); }); ``` You now have a fully functionaing WhatsApp bot! Send anything you want to know about your bot ๐Ÿค– and you should see it respond super fast ๐Ÿ’ฌ โšก Adding welcome messages and a little formatting is quite simple, look at the final [repl](https://repl.it/@jajoosam/wikibot) to see how I did it ๐Ÿ‘จโ€๐Ÿ’ป ## ๐Ÿ”— Sharing the bot For others to use this bot, they'll need to join your sandbox first - and send a message just like you did earlier ๐Ÿ‘‰ `join <two-words>` You can create links with this text too - For example this link lets you join my bot ๐Ÿ‘‡ ``` https://wa.me/14155238886?text=join ultramarine-tapir ``` `14155238886` is my bot's number, while `ultramarine-tapir` is the sandbox phrase. ## โšก What's next? Now that you know how to build a bot on WhatsApp, try sending notifications to yourself, and building more useful tools! Twilio has loads of [other mediums](https://www.twilio.com/channels) to message through too! All code for my WikiBot is on [Github](https://github.com/jajoosam/wikibot)! I'm a 15 year old maker ๐Ÿ‘จโ€๐Ÿ’ป For more cool things to make and to stay update with my progress, sign up for [my newsletter ๐Ÿ“ง](https://buttondown.email/jajoosam)
14
posted by jajoosam (410) 5 months ago
โ–ฒ
4
๐Ÿš€ Using Webpack To Bootstrap A JS App (Express)๐Ÿ’ 
# ๐Ÿš€ Using Webpack To Bootstrap A JS App (Express)๐Ÿ’  Webpack is an integral part of modern JavaScript tooling. It bundles all your JavaScript modules and has plugins for bundling CSS, JSX, and many other files. It's used for creating React and Vue single page applications and much more! We're going to be manually configuring Webpack to work with an Express server. So let's get started! ๐Ÿ‘ ### Prerequisites - Intermediate experience with JavaScript - Understand basic Webpack concepts # Getting Started Typically we create our projects (in development) using [webpack-dev-server](https://github.com/webpack/webpack-dev-server). But, because we don't have access to `npm` script commands (ex. `npm run start`) in repl.it, ~~we're going to be using Webpack from an Express app.~~ **Important Edit:** You can easily use `webpack-dev-server` by instantiating an instance of it, rather than using a CLI command. Find the guide [here](https://repl.it/talk/learn/Using-Webpack-To-Bootstrap-A-JS-App-webpack-dev-server/12209). **I highly recommend using `webpack-dev-server` directly rather than trying to use some middleware with Express.** Let's start off with a super basic express app. ```js // index.js let express = require("express"); let app = express(); app.get("/", (res, req) => req.send(`<p>Working</p>`)) let port = process.env.PORT || 3000; app.listen(port, () => console.log(`App listening on ${port}`)); ``` Remember to add `express` as a dependency. ## Adding Webpack ![webpack](https://i.imgur.com/e0XFRGh.gif) We're using `webpack-dev-middleware` so we can use Webpack in this Express app. So, add `webpack-dev-middleware` and `webpack` to your dependencies and require them in your `index.js` ```js let webpack = require("webpack"); let webpackDevMiddleware = require("webpack-dev-middleware"); ``` Now, let's create a configuration file for Webpack. We're going to be including all the standard stuff. Be sure to add `friendly-errors-webpack-plugin` and `html-webpack-plugin` to your `package.json`. ```js // webpack.config.js const path = require("path"); const webpack = require("webpack"); const FriendlyErrorsWebpackPlugin = require("friendly-errors-webpack-plugin"); const HtmlWebpackPlugin = require("html-webpack-plugin"); module.exports = { mode: "development", devtool: "cheap-module-eval-source-map", entry: { main: path.resolve(process.cwd(), "src", "main.js") }, output: { path: path.resolve(process.cwd(), "dist"), publicPath: "/" }, watchOptions: { // ignored: /node_modules/, aggregateTimeout: 300, // After seeing an edit, wait .3 seconds to recompile poll: 500 // Check for edits every 5 seconds }, plugins: [ new FriendlyErrorsWebpackPlugin(), new webpack.ProgressPlugin(), new HtmlWebpackPlugin({ template: path.resolve(process.cwd(), "public", "index.html") }) ] } ``` * [`friendly-errors-webpack-plugin`](https://github.com/geowarin/friendly-errors-webpack-plugin) outputs more helpful Webpack errors * [`html-webpack-plugin`](https://webpack.js.org/plugins/html-webpack-plugin) creates an HTML5 file and injects `<script></script>`tags with our Webpack bundles. Recall in our config we created a bundle called `main`, which is resolved to `./src/main.js`(this is the Webpack bundle we're injecting). And, because we're adding a `template` property, it means that `html-webpack-plugin` will use our HTML file, then inject the script tags * the `new webpack.ProgressPlugin(),` inside `plugins: []` gives Webpack's progress building your files (in the terminal) * I chose `cheap-module-eval-source-map` for the property `devtool` because it provides fast compilation with source maps. Read more in the [documentation](https://webpack.js.org/configuration/devtool/#root) * `watchOptions` prevents Webpack from recompiling everything too much (and lagging your replit). See the inline comments and [documentation](https://webpack.js.org/configuration/watch) for more info Now, let's create our `src/main.js` and `public/index.html` that we referenced in the Webpack config! For the `index.html`, I've included all the standard HTML boilerplate with the addition of a `div` with an `id` of `app`. ```html <!-- public/index.html --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Simple Webpack Boilerplate</title> </head> <body> <div id="app"></div> </body> </html> ``` For the `main.js` I do some simple DOM manipulation and logging. This is where your application really starts. ```js // src/main.js console.log("Delta Echo Foxtrot"); document.getElementById("app").innerHTML = "Golf Hotel India"; ``` Now, this won't do anything if we don't use the `webpack-dev-middleware` in our Express app. So, inside `index.js`, add it. First, let's add our Webpack configuration file. According to the [documentation](https://github.com/webpack/webpack-dev-middleware) for `webpack-dev-middleware`, we add the following to create a simple configuration. ```js let webpackConfig = require("./webpack.config"); let webpackCompiler = webpack(webpackConfig); let webpackDevMiddlewareOptions = { publicPath: webpackConfig.output.publicPath }; app.use(webpackDevMiddleware(webpackCompiler, webpackDevMiddlewareOptions)); ``` So, our final `index.js` should look like the following ```js // index.js let express = require("express"); let app = express(); let webpack = require("webpack"); let webpackDevMiddleware = require("webpack-dev-middleware"); let webpackConfig = require("./webpack.config"); let webpackCompiler = webpack(webpackConfig); let webpackDevMiddlewareOptions = { publicPath: webpackConfig.output.publicPath }; app.use(webpackDevMiddleware(webpackCompiler, webpackDevMiddlewareOptions)); let port = process.env.PORT || 3000; app.listen(port, () => console.log(`App listening on ${port}`)); ``` And huzzah! We get "Golf Hotel India" on the web page. ![finalResult](https://storage.googleapis.com/replit/images/1552775116994_6275b38102cadbc774f653095b94bbd7.pn) This is where the tutorial ends but where your Webpack application starts! Now you can use whatever plugins and loaders you need to create your applications! Note that if you're creating an SPA (Single Page Application), you will have to do some extra configuration for HMR ([Hot Module Replacement](https://webpack.js.org/concepts/hot-module-replacement) (using [webpack-hot-middleware](https://github.com/webpack-contrib/webpack-hot-middleware))) and HistoryAPI (using [connect-history-api-fallback](fallback) is simplest).
0
posted by eankeen (48) 6 days ago
โ–ฒ
30
Build your very own URL shortener ๐Ÿ”—๐Ÿš€
## Build a tiny URL shortener, using a remote database ![](https://boring-lamport-1b901a.netlify.com/2018-12-2214-5f2c1bbe-59a2-4ba7-9544-438b1ab5e1dd.39.01.gif) [Demo](https://l.4ty2.fun) โฏ๏ธ [Code](https://repl.it/@jajoosam/tyni) ๐Ÿ‘จโ€๐Ÿ’ป Setting up a URL shortener is a lot of work - either you have to pay, or spend hours setting up your own server. This is a guide to making your own URL shortener with [repl.it](http://repl.it) - using `express`, and a remote database - all on `node.js` ## ๐Ÿ› ๏ธ Getting our environment running First up, fork the [https://repl.it/@jajoosam/tyni-starter](https://repl.it/@jajoosam/tyni-starter) repl, so that you have a running project. Next, create a new file - `.env` ![](https://boring-lamport-1b901a.netlify.com/Untitled-675a864b-7c8c-487d-8ca7-595eb8af67ba.png) A `.env` file can store secrets for you, that no one else will be able to see. This is where we want to store our token for accessing the remote database. ## ๐Ÿ“ Making our database We're going to be using [jsonstore.io](http://jsonstore.io) for storing all our URLs. Head over to [jsonstore.io/get-token](https://www.jsonstore.io/get-token) - and copy the token you see - this is the secret we want to store in our `.env` file. Open up your `.env` file, and set `KEY` to your token, like this ๐Ÿ‘‡ ```bash KEY=yourTokenGoesHere ``` Remember to keep **no whitespace**, or your token might not be recognized right! When you open `index.js` you'll see that I've already initialized the database, and a small web server for you. Now let's get to making our API so we can shorten them URLs ๐Ÿš€ ## ๐Ÿ‘จโ€๐Ÿ’ป The API There are two parts to our URL shortener: 1. Storing the short link, corresponding to the long URL - in our database. 2. Redirecting visitors from the short link to the long link Both of these are super simple to implement, thanks to the `express` server we're using - we'll just be collecting `get` requests for both of the tasks. For adding links to our database, we have a special endpoint - requests to it have two parts: the long URL, and the short path. ```javascript app.get('/shorten', (req, res) => { db.write(req.query.key, {"url": req.query.url}); res.status(200); }); ``` Adding this to our code lets us correspond the short path (`key`) to the long `url`, and then we finally send a successful response. For the second task, we'll just be collecting the short path (`key`) from a request, finding the corresponding URL in our database, and then redirecting our visitor โฌ‡๏ธ ```javascript app.get('/:key', (req, res) => { db.read(req.params.key + "/url").then( (url) => { res.redirect(url); }); }); ``` That's prety much it - we have a fully functional URL shortener ๐Ÿคฏ - check it out for yourself, open a new URL which looks like this ๐Ÿ”— ``` https://tyni.jajoosam.repl.co/shorten?key=yay&url=https://dsh.re/50854 ``` Now, going to [`http://tyni.jajoosam.repl.co/yay`](http://tyni.jajoosam.repl.co/yay) will be nice to see ๐Ÿ‘‡ ![](https://boring-lamport-1b901a.netlify.com/1-8380b52f-92c2-4acc-87dc-64011c6fd502.jpg) Of course, you'll be replacing `tyni.jajoosam` in those URLs with your own repl! ## โœจ The Frontend Our URL shortener does work, but it's tedious, having to type out a huge URL before shortening it - we can make the whole process much simpler with a simple frontend. I've already created this - and gone for a neat and minimal look using [wing.css](https://github.com/kbrsh/wing) ![](https://boring-lamport-1b901a.netlify.com/Untitled-0fa3af54-ddae-46ca-abdf-4fcbcb218f8b.png) You just have to add code to send visitors to the hompage in the `static` folder ๐Ÿ  ```javascript app.get('/', (req, res) => { res.sendFile("static/index.html", {root: __dirname});; }); ``` If you browse through the `static` folder, you'll find a simple `HTML` file with a form, `CSS` to style our page, and most importantly, `JS` to send requests to our URL shortening API. The `HTML` is quite straightforward, we're asking for the long URL, and *optionally* a short path. Open up `script.js` and you'll see the `shorten()` function. Here's what the JS file does (*I've also annotated the code with comments*) ๐Ÿ‘‡ ๐Ÿ” Getting the path(`key`) and the long `url` from the form. ๐Ÿ“ Possibly generating a random 5 character hash as our path (in case there's no path entered) ๐Ÿ”— Sending a get request to our API, with our `key` and `url` as parameters ๐Ÿ–ฅ๏ธ Displaying the shortened URL on our page ## ๐ŸŒ Getting our custom domain Sure, our links are shorter - but we still don't have them on our own domain, and the `repl.co` links can be pretty long ๐Ÿ‘€ Luckily for us, the folks at [repl.it](http://repl.it) recently allowed custom domains to be used! That means this project could be something you actually use all the time ๐Ÿ˜„ Check out `dotcomboom`'s guide on [using custom domains](https://repl.it/talk/learn/How-to-use-a-custom-domain/8834), it should only take a few minutes. It also teaches you about getting free domains ๐Ÿ’ธ Be sure to put down any questions or improvements down in the comments ๐Ÿ’ฌ - and here's all the code for you to go over again ๐Ÿค– https://repl.it/@jajoosam/tyni
10
posted by jajoosam (410) 3 months ago
โ–ฒ
69
A Crash Course in LOLCODE ๐Ÿฑ
# A Crash Course in LOLCODE ___ OHAI! This is a crash course in the beautiful programming language known as LOLCODE! LOLCODE is a great language, and while it is similar to many other programming languages, trying to learn it may confuse some people because of the syntax. For this tutorial, any time I say `[variable1]`, `[variable2]`, or `[variable]`, as long as I'm not talking about initializing a variable, you can put a value instead. ### Creating a new program To begin a program, you need to have the line `HAI 1.2`. This will declare that it's a LOLCODE program that's written in LOLCODE 1.2. The last line in the program must be `KTHXBYE`. ### Comments There are two different ways of doing commenting - `BTW` and `OBTW`. The differences are shown below: ```lolcode BTW This is a one-line comment. OBTW This is a multi-line comment TLDR ``` The indentation is not necessary, but it makes it easier to read. ### Variables Variables are dynamically typed in LOLCODE, so you don't have to give them a type when declared. To declare a variable, use `I HAS A [variable]`. However, if you do want to give it a type, you can use `I HAS A [variable] ITZ A [type]`. There are 4 usable types of variables: - TROOF (a boolean - `WIN` or `FAIL`, corresponding to true or false.) - NUMBR (an integer - whole numbers) - NUMBAR (a float - decimal places) - YARN (a string - text, defined by "") Variable names are case-sensitive, so INT is different from Int. You can use capital and lowercase letters, underscores, and numbers - as long as neither underscores nor numbers begin the variable name. To assign one variable to another, use `[variable 1] R [variable 2]`. ### Concatenation It's very simple to concatenate YARNS in LOLCODE - you use `SMOOSH [variables to concatenate, seperated by AN] MKAY`. It will cast any input given to it to a YARN before concatenating. See below for an example. ```lolcode I HAS A VAR1 ITZ "Hi" I HAS A VAR2 ITZ 1234 I HAS A VAR3 ITZ WIN I HAS A VAR4 ITZ SMOOSH VAR1 AN VAR2 AN VAR3 MKAY VISIBLE VAR4 BTW The output will be Hi1234WIN ``` ### Casting There are a couple different ways of casting a variable from 1 type to another. The first is `MAEK [variable] A [type]`. This will attempt to cast from whatever type the variable is to the desired type. However, this will not work if it's illogical - for instance, trying to cast letters into a NUMBR or NUMBAR. To cast a variable to a different type and save the output in a different variable, use `[variable 1] R MAEK [variable 2] A [type]`. ### Expressions There are 3 different types of expressions in LOLCODE - **Math**, **Boolean**, and **Comparison**. The basic form for all expressions is either `[expression] [variable]` or `[expression] [variable1] AN [variable2]`. #### Math In LOLCODE, you have all the typical expression types - addition, subtraction, multiplication, division, and modulus (remainder), as well as some less-common ones - min (returns the value of the smaller of 2 variables) and max (returns the value of the larger of 2 variables). If either variable is a YARN and has a decimal, it is cast to a NUMBAR for the calculation. If it doesn't have a decimal, it's cast to a NUMBR. If both variables used are NUMBRs, then integer math is performed. If one or both are NUMBARS, floating floating point math is invoked. See below for a list of math expressions: ```lolcode SUM OF [variable 1] AN [variable 2] BTW This is addition DIFF OF [variable 1] AN [variable 2] BTW This is subtraction PRODUKT OF [variable 1] AN [variable 2] BTW This is multiplication QUOSHUNT OF [variable 1] AN [variable 2] BTW This is division MOD OF [variable 1] AN [variable 2] BTW This is modulus (remainder) BIGGR OF [variable 1] AN [variable 2] BTW This returns the bigger variable's value SMALLR OF [variable 1] AN [variable 2] BTW This returns the smaller variable's value ``` #### Boolean The boolean expressions work pretty much as you would expect, comparing WINs and FAILs. You can use and, or, xor, and not. Any value passed to this is cast to a TROOF. See below for the complete list: ```lolcode BOTH OF [variable 1] AN [variable 2] BTW This is an and statement EITHER OF [variable 1] AN [variable 2] BTW This is an or statement WON OF [variable 1] AN [variable 2] BTW This is an XOR statement NOT [variable] BTW This is a not statement ALL OF [variable 1] AN ... MKAY BTW This is an infinite and statement, keep adding variable names and ANs to check more ANY OF [variable 1] AN ... MKAY BTW This is an infinite or statement, see above ``` ### Comparisons This is very similar to boolean expressions - it takes in 2 variables and checks if they're either the same or different. However, it doesn't have to be 2 TROOFS, but they do have to be the same type. "9" and 9 will NOT be recognized as the same. See below for examples ```lolcode BOTH SAEM [variable 1] AN [variable 2] BTW returns WIN if variable 1 == variable 2 DIFFRINT [variable 1] AN [variable 2] BTW returns WIN if variable 1 != variable 2 BOTH SAEM [variable 1] AN BIGGR OF [variable 1] AN [variable 2] BTW variable 1 >= variable 2 BOTH SAEM [variable 1] AN SMALLR OF [variable 1] AN [variable 2] BTW variable 1 <= variable 2 DIFFRINT [variable 1] AN BIGGR OF [variable 1] AN [variable 2] BTW variable 1 < variable 2 DIFFRINT [variable 1] AN SMALLR OF [variable 1] AN [variable 2] BTW variable 1 > variable 2 ``` ### Printing To output text, you have to use the `VISIBLE [output]` command. This can also be used with variables by using `VISIBLE [variable]`. See below for examples: ```lolcode VISIBLE "Invisible" VISIBLE INT ``` ### Input To get input from the user, you can use `GIMMEH [variable]`. For this, you MUST specify a variable because that is where the output is stored. GIMMEH stores input as a YARN, so if you want to get a NUMBR or NUMBAR you have to cast it as such. ### Conditionals Creating conditionals is fairly straightforward in LOLCODE. There are 2 basic formats - one utilizing TROOFs, and one utilizing other types of variables. To create a conditional using TROOFs, use the following: ```lolcode [expression], O RLY? YA RLY BTW This code will execute if the result of [expression] is WIN NO WAI BTW This code will execute if the result of [expression] is FAIL OIC ``` To create a conditional using other variable types is a little more involved. Basically, `OMG [value]` is the same as checking if the expression is equal to [value], and `OMGWTF` is an else. To end a statement, you must put GTFO. ```lolcode [expression], WTF? OMG 5 BTW This code will execute if the result of [expression] is 5 GTFO OMG 91 OMG 21 BTW This code will execute if the result of [expression] is 91 or 21 GTFO OMGWTF BTW This code will execute if the result of [expression] is not 5, 91, or 21 OIC ``` ### Loops Loops are a somewhat confusing beast at first, but actually aren't that hard. First, you need `IM IN YR [label for the loop - I would recommend just calling it LOOP]`. Then, if you want to increase the iterator variable have `UPPIN YR [variable]`, and if you want to decrease the iterator variable have `NERFIN YR [variable]`. Finally, if you want to go until a certain value, use `TIL [expression]`, and if you want to go while a certain expression is true, use `WILE [expression]`. To end the loop, use `IM OUTTA YR [label]`. See below for an example: ```lolcode I HAS A ITERATOR ITZ 0 IM IN YR LOOP UPPIN YR ITERATOR TIL BOTH SAEM ITERATOR AN 9 VISIBLE ITERATOR IM OUTTA YR LOOP BTW This will output 0 through 8, and then stop before printing 9. ``` ### Conclusion Aaaand that's pretty much everything I could possibly find on the internet about LOLCODE... There is documentation for functions and BUKKITs (arrays), but I couldn't get them to work and so I decided against detailing them. If you still want MOAR LOLCODE documentation, go [here](https://github.com/justinmeza/lolcode-spec/blob/master/v1.2/lolcode-spec-v1.2.md), [here](https://esolangs.org/wiki/LOLCODE), or [here](https://learnxinyminutes.com/docs/LOLCODE/). If you want a fairly simple random python with turtle spiral generator, go check out my tutorial for that [here](https://repl.it/talk/challenge/Python-Turtle-Graphics-Random-Spirals/7651). If you want a super long tutorial about how to make your own game using only python with turtle graphics, go [here](https://repl.it/talk/challenge/How-to-make-a-fairly-basic-game-using-Python-with-Turtle-Graphics/8182). If you liked this tutorial, feel free to leave an upvote. Thanks! :)
11
posted by minermaniac447 (150) 5 months ago
โ–ฒ
35
How to use a custom domain
In case you've worked with repl.it before, you may have noticed that you are given a decently long subdomain for your project, like the following: `my-little-blog--dotcomboom.repl.co` You may have wanted to change that into a shorter, more memorable domain name, like this: `mylittleblog.cf` Let's see how. ## Getting your domain This section will cover registering a domain name with Freenom. If you already have a domain, [skip this part](https://repl.it/talk/learn/How-to-use-a-custom-domain/8834#attaching-to-repl.it). ### A word on Freenom #### You don't own it Freenom hands a selection of domain names out like free candy. If you've heard of dot.tk, it's these guys. In this tutorial I'll walk you through them, but I'd also like to point out that by registering a domain name with Freenom, you are still not entitled to it. It's perfectly valid and legal for them to all of a sudden deactivate your domain and use it to make money with ads, without notification. This is just a fair warning, some people have had domains from Freenom for a long time without any issues, but anything can happen. In short, nothing could be actually free. You are borrowing Freenom's domains for your purposes, and don't own them like you would from another registrar. #### HTTPS ~~If you use Freenom, you won't be able to use HTTPS. You need to use "http://" before your domain instead. You will still be able to use your repl.co domain name if you need to use HTTPS, so make note of that.~~ **As https://repl.it/@turbio pointed out, HTTPS does work with a Freenom domain! You just need to wait a little bit of time for it to get set up.** As for "mission critical" applications, like, say, Uptime Robot or something like that, you will probably want to use your repl.co domain. ### Registering the domain You will want to make or log into a Freenom account first. Then, you will be brought to the [Client Area](https://my.freenom.com/clientarea.php). Once you're signed in, you will find "Register a New Domain" under the Services menu. ![image](https://storage.googleapis.com/replit/images/1543204601000_441af0314e3fc5e71a0139e9f766e678.pn) This will bring you to Freenom's domain search. Type in the name you want. In this example, I searched for "mylittleblog". It will give you a list of what domains they have available. ![image](https://storage.googleapis.com/replit/images/1543204667654_d8c9142df0d4f1487abdc49631a435ad.pn) Choose the one you want, then scroll down to the bottom and choose "Checkout". Don't touch the section in the middle, we'll cover that later. Choose the period you'll be using the domain, which is from 3 months to 12 months. (If memory serves, when you're going close to the due date, you are offered the opportunity to renew.) ![image](https://storage.googleapis.com/replit/images/1543204740295_ac2737cabaa59f81f9e162774f281755.pn) Check the box to agree to the Terms and Conditions (reading them is a good idea too) and complete the order. Then, go back to your client area. ## Attaching to repl.it ### Repl.it setup Go to your repl and click on the pencil icon next to the address in the preview pane. ![image](https://storage.googleapis.com/replit/images/1543205234697_4f6cbafaf7aae5af99d0c74ae423b95f.pn) Type in your domain name and if the domain is registered correctly, it will prompt you to add a `CNAME` record to your domain: ![image](https://storage.googleapis.com/replit/images/1543205288588_5e41390b98167f4e887b0de892808666.pn) Copy where the record should point to, then go to your client area at your registrar (in my case, Freenom). ### Registrar setup From [My Domains](https://my.freenom.com/clientarea.php?action=domains), go to the management page for the domain you chose. From Management Tools, choose Nameservers. Make sure it is checked to use default nameservers. ![image](https://storage.googleapis.com/replit/images/1543205432252_a191c52c1f996716d91e8266a41efd7c.pn) Then, find the area where you can add records; for Freenom, it is "Manage Freenom DNS". In the Name textbox, enter your domain name, nothing more, nothing less. Change the type to `CNAME`. Then, set the target to what repl.it asked you. Then, choose Save Changes. ![image](https://storage.googleapis.com/replit/images/1543205550116_8df6cb0139894e925b9a6fb80ea454aa.pn) ### Finish Now go back to your repl. Repl.it now should have noticed that your domain is set up correctly and now lets you click on the Link button. ![image](https://storage.googleapis.com/replit/images/1543205653984_83f953b10c6a8cbea3959c8f3709b06d.pn) You're done! Your domain should now be attached to your repl.
16
posted by dotcomboom (43) 4 months ago
โ–ฒ
8
๐Ÿ‘ฉโ€๐Ÿš€ Improve Performance with Replcator ๐Ÿค˜
# Improve Performance with Replcator Ever wanted to improve the performance of your repl that more than one person uses, or quickly create a load balancer? Well, you're in luck. I recently created Replcator, a project that lets you load balance your repl in very few steps, while keeping development easy. ## Get Started First, you'll need some repl to load balance. Any repl that displays a website is ideal. 1. Fork [the Replcator Repl](https://repl.it/@FelixMattick/Replcator-v2) and rename it. 2. Get your `connect.sid` cookie. If you're using Chrome, get [this extension](https://chrome.google.com/webstore/detail/editthiscookie/fngmhnnpilhplaeedifhccceomclgfbg?hl=en) to easily view cookies. **Do not share this cookie with anyone! People can use it to access your account.** 3. In the repl you forked, create a file called `.env`. 4. Set it's contents to `SID=sid-here` except replace `sid-here` with the value of your `connect.sid` cookie. Don't worry, nobody will be able to see this. 5. Go to the `config.js` file and edit it with the correct information. ## Config.js Here's the things in your `config.js` that you should edit. - `count`: This is the number of repls that are going to be spawned and load balanced across. Due to annoying ratelimits, you should probably keep the max at 10 for now. - `user`: Your own username. - `repl`: The path to the repl you want to load balance. It should be in the format `@User/Repl-Name`. Please replace all spaces with dashes. I'll leave the rest out for now, but there should be comments with explanations next to them in the file. ## You're Done! All you have to do is click run, and lo and behold you should have a fully functional load balancer. If you get an error, please tell me in the comments, as Replcator is in beta and I'd love to improve it. ## Replcator Features Here is an incomplete feature list of Replcator. - Automatically duplicates and deploys your Repl, so you don't have to manually create and update a bunch of instances - Scans all your instances periodically to look for errors, and automatically restarts if there is one - Sets custom HTTP headers with extra info about the current Replcator instance - Super simple and easy to use, can be set up in 5 minutes I'd love to hear from you guys in the comments below!
1
posted by FelixMattick (38) 22 days ago
โ–ฒ
16
Build an ML app, with just a little JavaScript ๐Ÿค–
## Making a feature extractor with ml5.js and p5.js ![](https://ecstatic-rosalind-da34e4.netlify.com/main.gif) [Demo](https://feature-extractor--jajoosam.repl.co) โฏ๏ธ [Code](https://repl.it/@jajoosam/feature-extractor) ๐Ÿ‘จโ€๐Ÿ’ป [Video Walkthrough](https://ecstatic-rosalind-da34e4.netlify.com/feature-extractor-demo-compressed-3e7aac1a-1c37-4448-92ab-140b5dd84056.mp4) ๐ŸŽฆ Machine Learning is a super cool way to build AI apps - but I won't lie, it is a lot of math. Luckily, thanks to [ml5.js](https://ml5js.org) - we don't need to spend months understanding ML, we can apply it and develop some cool uses in our app within just a few hours ๐Ÿ˜„ This is a guide to build a feature extractor - something that can get a video stream from your webcam and predict what it sees, once it is trained with a little bit of data - just like in the video above! ## ๐Ÿด Forking the frontend I don't want to waste too much of your time with the frontend I used for this app - I think it'd be much better if I skipped right to the JavaScript, and logic. You should fork this repl - [https://repl.it/@jajoosam/feature-extractor-start](https://repl.it/@jajoosam/feature-extractor-start) - so that you have the HTML already there! It's essentially just a few `div`s for holding the video and some other text, along with some buttons to control our app! I've commented everything (except the CSS ๐Ÿ˜›) - so you should be able to go through the frontend and understand the basic layout of the app in a few minutes. Once you're done, head over to `script.js` ![](https://ecstatic-rosalind-da34e4.netlify.com/Untitled-8e918105-6ae0-4591-8362-7ed6e7068460.png) ## โœ๏ธ Declaring the variables We're going to have quite a few variables to declare, so the first thing we're going to do is create them. Our app is going to have a lot of functions, and it's important that all of them can access the variables - which is why we shall declare right at the start of our code ```javascript var featureExtractor, classifier, video, loss, redCount, blueCount; redCount = blueCount = 0; ``` I'll give you an overview of what these are for, but you'll understand them much better as we continue building our app. `featureExtractor` and `classifier` are variables we're going to store and initialize machine learning models in. `video` is where we're storing the webcam stream, while `loss` lets us know the progress of how far our on feature extractor has been trained. Finally, `blueCount` and `redCount` are counters for how many images there are in each category - and we initalize both of them with a value of `0`, in the next line. ## ๐Ÿ› ๏ธ The `setup()` `setup()` is a function which shall fire up as soon as our code is ready to run. Bacause of using [p5.js](https://p5js.org), our code is pretty readable and easy to understand here. ```javascript function setup() { // Tells p5 to not automatically create a canvas element. noCanvas(); // Starts capturing a video feed from the webcam video = createCapture(VIDEO); // Puts the video stream into the div in our html, with ID `video` video.parent('video'); // Initializes a feature extractor, yet to be trained - from ml5.js featureExtractor = ml5.featureExtractor('MobileNet'); classifier = featureExtractor.classification(video); // What we're doing next - setting up buttons! setupButtons(); } ``` This code goes in your `script.js` file right after you declare variables - essentially, it gets a video stream and displays it on our page, inside a `div` with the ID `video`. We also make some functions using the ml5.js library here, and pass the captured video as a parameter - you'll see what we do with these soon, but there's still a little more setup left! As you can see, at the end of `setup()`, we call `setupButtons()` - the function we're going to make next. Here, we're adding event listeners to buttons in our HTML - so we can run functions when they're clicked. Here's all the code we write for the `setupButtons()` function ๐Ÿ‘‡ ```javascript // A function to create the buttons function setupButtons() { buttonA = select('#red'); buttonB = select('#blue'); โ€‹ โ€‹ buttonA.mousePressed(function() { โ€‹ redCount++; โ€‹ classifier.addImage('red'); โ€‹ select('#redCount').html(redCount); โ€‹ }); โ€‹ โ€‹ buttonB.mousePressed(function() { โ€‹ blueCount++; โ€‹ classifier.addImage('blue'); โ€‹ select('#blueCount').html(blueCount); โ€‹ }); โ€‹ train = select('#train'); train.mousePressed(function() { classifier.train(function(lossValue) { // This is where we're actually training our model if (lossValue) { loss = lossValue; select('#info').html('Loss: ' + loss); } else { select('#info').html('Done Training! Final Loss: ' + loss); select('#train').style("display", "none"); select('#predict').style("display", "inline"); } }); }); // Predict Button buttonPredict = select('#predict'); buttonPredict.mousePressed(classify); } ``` That's the largest art of our app, and I'm going to break it up into several parts to make it easier to explain! ## ๐Ÿง™โ€โ™‚๏ธbUt wHaT Do tHeSe bUtToNs dO?!?!?! DON'T PANIC. Let's start off with this block: ```javascript buttonA = select('#red'); buttonB = select('#blue'); buttonA.mousePressed(function() { redCount++; classifier.addImage('red'); select('#redCount').html(redCount); }); buttonB.mousePressed(function() { blueCount++; classifier.addImage('blue'); select('#blueCount').html(blueCount); }); ``` `buttonA` and `buttonB` are nothing but the two different buttons we have in our app! ![](https://ecstatic-rosalind-da34e4.netlify.com/Untitled-c85ebc09-ab40-4060-a9ce-98966a9fe021.png) With `.mousePressed()` we're defining what happens when any of these buttons are pressed - which is: - Increase the count by 1, using the `++` operator - Capture the current frame from the webcam videom and add it to the classifier, with `classifier.addImage()` - Update the count on our app by changing the button's text, with `.html()` Next up, we have this whole block - where we train the classifer itself: ```javascript train = select('#train'); train.mousePressed(function() { classifier.train(function(lossValue) { // This is where we're actually training our model if (lossValue) { loss = lossValue; select('#info').html('Loss: ' + loss); } else { select('#info').html('Done Training! Final Loss: ' + loss); select('#train').style("display", "none"); select('#predict').style("display", "inline"); } }); }); ``` When the `Train ๐Ÿš‹` button on our app is pressed, we call `classifier.train()` - and with each iteration, the function we supply there is called - which is why we see the *[Loss](https://ml-cheatsheet.readthedocs.io/en/latest/loss_functions.html)* value keep changing. ![](https://ecstatic-rosalind-da34e4.netlify.com/CleanShot-2019-01-26-at-18-6a9809c8-75a3-4b88-b1ee-3931aa15d08c.04.57.gif) When the *Loss* value is `0`, then we hid the `Train ๐Ÿš‹` button, and show the previously hidden `Predict ๐Ÿ”ฎ` button! The last 2 lines of the `setupButtons()` function are about the predict button: ```javascript buttonPredict = select('#predict'); buttonPredict.mousePressed(classify); ``` It seems like we're calling this `classify` function - which is what we're going to build next! Keep going, we're almost done ๐Ÿ’ฏ ## ๐Ÿ”ฎ Predictions, and showing results Our `classifier()` function is pretty straightforward - this is all we do: ```javascript function classify() { classifier.classify(gotResults); } ``` We're just telling the ml5 `classifier` to classify the current frame in either of the categories (๐Ÿ”ด or ๐Ÿ”ต), and send the results to the `gotResults` function - which brings us to the final part of our app! As long as the classifier doesn't send us an error, we change the entire page's background color to either `red` or `blue` - and then call `classify()` again, which keeps our code running forever, and the predictions keep coming! ```javascript function gotResults(err, result) { if (err) { console.log(err); } select("body").style("background", result); classify(); } ``` That's all the code we have to write to build the example feature extractor. I highly recommend going through the final code so that you can see how everything fits together well - I know there are quite a few functions too keep track of, but I've commented everything to make it easier ๐Ÿ˜… https://repl.it/@jajoosam/feature-extractor Now go ahead, try it out! Here are some things I did to see if the feature extractor would work (it did!) ๐Ÿ‘‡ - Keep my head in the left in ๐Ÿ”ด, and right in ๐Ÿ”ต - Make a โœŒ๏ธ in ๐Ÿ”ด, and a โœ‹ in ๐Ÿ”ต - Wear different colored ๐Ÿ‘•s in ๐Ÿ”ด and ๐Ÿ”ต Let me know other cool things it worked with ๐Ÿ‘€ ## โœจ okay, but what do I **MAKE** with this? There are SOOOOO MANY THINGS you could do! A feature extractor gives you the ability to add this whole other, mysterious way of controlling, and getting input in your app! Here are a few ideas ๐Ÿ’ก - A gesture controlled game, right in the browser ๐ŸŽฎ - A mood tracker - leave the tab open all day, and train it with different face expressions ๐Ÿค” - Build your own **[hotdog or not](https://www.youtube.com/watch?v=pqTntG1RXSY)** app ๐ŸŒญ - Build an app which only lets you message others with gestures ๐Ÿ’ฌ ## ๐Ÿ‘จโ€๐Ÿซ what else is there? ml5.js can do crazy things! You can integrate image classification into your own app [like I did](https://repl.it/talk/challenge/male-A-scavenger-hunt-with-image-classification/10185), find similar words which fit into a text, get a precise skeleton of human bodies and even transfer the styles in images - look at what my picture looks like, when composed with the style of a cool painting ๐Ÿ–Œ๏ธ ![mepaint](https://ecstatic-rosalind-da34e4.netlify.com/mepaint.png) You should take a look at the many [examples on the ml5.js website](https://ml5js.org/docs/quick-start) - or take a look at [Dan Shiffman's videos on ml5.js](https://www.youtube.com/playlist?list=PLRqwX-V7Uu6YPSwT06y_AEYTqIwbeam3y), he covers loads of things you can do with it - and explains everything with a ton of energy. I highly recommend watching his videos on the [Coding Train](https://www.youtube.com/user/shiffman) YouTube! You have over 2 weeks left to submit something to the [Repl.it AI Challenge](https://repl.it/talk/challenge/Artificial-Intelligence-Competition/10058) ๐Ÿ“† If you have any questions or need any help with your ml5 submission, feel free to ping me @jajoosam on the Discord, or leave something in the comments :)
1
posted by jajoosam (410) 2 months ago
โ–ฒ
13
What tutorials would you like to see here?
Hey all, Last year I wrote several tutorials and shared them here. These tutorials were on [Discord Bots](https://www.codementor.io/garethdwyer/building-a-discord-bot-with-python-and-repl-it-miblcwejz), building a [Django Application](https://www.codementor.io/garethdwyer/creating-and-hosting-a-basic-web-application-with-django-with-repl-it-lohsyub20), [Web Scraping](https://www.codementor.io/garethdwyer/beginner-web-scraping-with-python-and-repl-it-nzr27jvnq), and [Algorithms](https://www.codementor.io/garethdwyer/quicksort-tutorial-python-implementation-with-line-by-line-explanation-p9h7jd3r6). I'm preparing some new topics to write about this year and I'd love to get your input on what kind of tutorials you'd like to see here. Please comment below with either (or both!) * Topics you'd like to see more of, for example - Data Science, Machine Learning, Web Development, Data Structures and Algorithms, Information Security (hacking), specific languages and frameworks, or any other area that you're interested in and would like to read and work through tutorials on. * Specific titles and topics - if you're battling with something specific, feel free to drop an exact title you'd like to see, like "Setting up a Flask web app on Repl with a PostgreSQL database hosted on AWS" If you already see the topic in the comments below, feel free to upvote it instead of duplicating. Looking forward to publishing more posts soon!
15
posted by GarethDwyer1 (88) 2 months ago
โ–ฒ
10
Python - Discrete Math Logic Parser
# (Python) Discrete Math Logic Parser ## What is Discrete Math Logic? Discrete Math Logic is a basic form of logic. In its simplest form, it can be represented by *p ^ q* In discrete math, the basic operators are: * ^ (which denotes AND) * v (which denotes OR) * ~ (which denotes NOT) There are more operators that exist but for this tutorial, we will only cover these simple ones. If you want to include the other operators, I will have them at the end of this post along with the code for each operator. ## Requirements * Basic Knowledge of Python * Classes * Functions * Knowledge of Recursion ## Logic Parser Layout Now for this tutorial, we are going to have 4 different python files. * `errors.py` which will hold all the `Exception`s we will use * `logic_var.py` which will hold the class for a variable such as `a` or `b` * `logic_node.py` which will be the basics for a left and right logical expression such as `a ^ b` * `logic_tree.py` which will be the primary class for a logical expression Now we can get started with the tutorial. ___ ### `errors.py` Now this is the simple stuff. All we really have in this file are just four simple classes that extend Python's `Exception` object. ```py class UnbalancedParentheses(Exception): pass class MissingTruthValue(Exception): pass class OperatorMismatch(Exception): pass class MissingValue(Exception): pass ``` And we're done with that part! That was easy. ___ ### `logic_var.py` In this file, we have a `LogicVar` class that will hold all information about a single variable in a logical expression. This class is very simple as we only have 2 attributes that a `LogicVar` object holds: * Whether or not it has a ~ (NOT) operator attached to it * The variable itself I'm going to layout the contents of each set of functions so hopefully you understand it better. ```py class LogicVar: def __init__(self, value = None, has_not = False, *, var_dict = None): # If the value is given, we are loading it in by the value and has_not parameters if value != None: self._value = value self._has_not = has_not # If the var_dict is given, we are recursively initializing this LogicVar. elif var_dict != None: # Try loading the value key from var_dict try: self._value = var_dict["value"] except: raise MissingValue("Required key for LogicVar. Must have \"value\" key in var_dict.") # Try loading the has_not key from var_dict try: self._has_not = var_dict["has_not"] except: self._has_not = has_not # If both value and var_dict are missing, we raise an Exception elif value == None and var_dict == None: raise MissingValue("Required variable for LogicVar. Must use either value or var_dict.") def __str__(self): if self.has_not(): return "~" + self.get_value() return self.get_value() def __eq__(self, value): if not isinstance(value, LogicVar): return False return ( self.get_value() == value.get_value() and self.has_not() == value.has_not() ) ... ``` All these functions are built-in functions for Python classes. The `__eq__` function is overridden so we can compare a `LogicVar` object with some other object later on. The `__str__` function is overridden so we can easily print out a `LogicVar` object when we print the parsed expression. ```py ... def get_value(self): return self._value def has_not(self): return self._has_not ... ``` These functions should be pretty straight forward. They are just getter methods for a `LogicVar` object. ```py ... def evaluate(self, truth_value = {}): if self.get_value() not in truth_value: raise MissingTruthValue("Required truth value for the variable \"{}\"".format(self.get_value())) if self.has_not(): return not truth_value[self.get_value()] return truth_value[self.get_value()] def get_truth_values(self, truth_values = []): # Keep track of evaluations evaluations = [] # Only run this if there is a has_not if self.has_not(): # Iterate through truth values for truth_value in truth_values: # Evaluate the value given by this LogicVar evaluation = { "expression": str(self), "truth_value": truth_value, "value": self.evaluate(truth_value) } # Only add the evaluation if it doesn't already exist if evaluation not in evaluations: evaluations.append(evaluation) return evaluations ``` Now the `evaluate` function will be given a dictionary of truth values to use to evaluate. For example, let's say that there is a `LogicVar` object whose variable is `a`, when the `evaluate` function runs, there is a `truth_value` given to it such as: ```py { "a": True, "b": True } ``` A truth value dictionary just holds the truth values for all the variables that exist in a logical expression. The `get_truth_values` function will run through all the possible truth values that exist for a logical expression. It depends on the amount of variables where this comes from. To explain better, let's say we are given an expression such as `a ^ b` All the possible truth values include: ``` | a | b | |---+---| | T | T | | T | F | | F | T | | F | F | ``` So there are 2โฟ possible combinations of truth values and that is where the list of truth values comes from. ___ ### `logic_node.py` Now this file holds the information behind a *logical expression* such as `a ^ b`. In that expression, `a` would be the left side, `b` would be the right side, and `^` would be the operator. This is exactly what the `LogicNode` class holds ```py class LogicNode: def __init__(self, operator = None, left = None, right = None, has_not = False, *, node_dict = None): # If operator, left, and right are given, we are loading it in by those parameters if operator != None and left != None and right != None: self._operator = operator self._left = left self._right = right self._has_not = has_not # If node_dict is given, we are recursively initializing this LogicNode. elif node_dict != None: # Try loading operator try: self._operator = node_dict["operator"] except: raise MissingValue("Required key for LogicNode. Must have \"operator\" key in node_dict.") # Try loading left try: self._left = node_dict["left"] except: raise MissingValue("Required key for LogicNode. Must have \"left\" key in node_dict.") # Try loading right try: self._right = node_dict["right"] except: raise MissingValue("Required key for LogicNode. Must have \"right\" key in node_dict.") # Try loading has_not try: self._has_not = node_dict["has_not"] except: self._has_not = has_not # If operator, left, right, and node_dict are all missing, raise an Exception elif (operator == None or left == None or right == None) or node_dict == None: raise MissingValue("Required variable(s) for LogicNode. Must use either operator, left, and right or node_dict.") # Turn the left and right values into either `LogicNode` or `LogicVar` objects if isinstance(self._left, dict): if "value" in self._left: self._left = LogicVar(var_dict = self._left) else: self._left = LogicNode(node_dict = self._left) if isinstance(self._right, dict): if "value" in self._right: self._right = LogicVar(var_dict = self._right) else: self._right = LogicNode(node_dict = self._right) def __str__(self): left = str(self.get_left()) right = str(self.get_right()) # Only put parentheses around expressions that don't have a ~ (NOT) operator attached to it if isinstance(self.get_left(), LogicNode) and not self.get_left().has_not(): left = "(" + left + ")" if isinstance(self.get_right(), LogicNode) and not self.get_right().has_not(): right = "(" + right + ")" operator = self.get_operator() if self.has_not(): return "~({} {} {})".format( left, operator, right ) return "{} {} {}".format( left, operator, right ) def __eq__(self, value): if not isinstance(value, LogicNode): return False return ( self.get_left() == compare.get_left() and self.get_right() == compare.get_right() and self.get_opoerator() == compare.get_operator() and self.has_not() == compare.has_not() ) ... ``` Again, this just sets up the `LogicNode` class and any object that is an instance of it. Notice that in the `__init__` method, the `left` and `right` parameters would normally take in a `LogicNode` or `LogicVar` object as the parameter. The way I will show how to load it in is using the `node_dict` keyword parameter.That will be shown in the next portion for the `LogicTree` class. ```py ... def get_operator(self): return self._operator def get_left(self): return self._left def get_right(self): return self._right def has_not(self): return self._has_not ... ``` More getter methods! There's really no need to explain this but I added it in just so there's not missing code for this tutorial. ```py ... def evaluate(self, truth_value = {}): left = self.get_left().evaluate(truth_value) right = self.get_right().evaluate(truth_value) if self._operator == "^": if self.has_not(): return not (left and right) return left and right if self._operator == "v": if self.has_not(): return not (left or right) return left or right def get_truth_values(self, truth_values = []): # Keep track of evaluations evaluations = [] # Get left evaluations left_evaluations = self.get_left().get_truth_values(truth_values) for left_evaluation in left_evaluations: if left_evaluation not in evaluations: evaluations.append(left_evaluation) # Get right evaluations right_evaluations = self.get_right().get_truth_values(truth_values) for right_evaluation in right_evaluations: if right_evaluation not in evaluations: evaluations.append(right_evaluation) # Iterate through truth values for truth_value in truth_values: # Evaluate this LogicNode object evaluation = { "expression": str(self), "truth_value": truth_value, "value": self.evaluate(truth_value) } # Only add the evaluation if it doesn't already exist if evaluation not in evaluations: evaluations.append(evaluation) return evaluations ``` Now the `evaluate` method in the `LogicNode` class will take into account the operator of the expression. So if the expression held in the `LogicNode` is an ^ (AND) operator, then it will evaluate the left recursively until it hits any `LogicVar` objects, and finally it will evaluate the right recursively. If there is a ~ (NOT) operator attached to the `LogicNode` object, it will handle that as well. The `get_truth_values` method will recursively evaluate both the left and right sides of the `LogicNode` object and then it will evaluate itself. Again, it will only add any evaluations that haven't already been made before. This is used when creating a truth table in the `LogicTree` class. It is helpful for sure when using it to evaluate a logical expression. ___ ### `logic_tree.py` Now comes the almost-grand finale of this tutorial: The `LogicTree` class which is the root of the expresssion parsing. ```py class LogicTree: def __init__(self, expression): self._expression = expression self._variables = [] self._root = None self.parse() def __str__(self): return str(self._root) def __eq__(self, value): if not isinstance(value, LogicTree): return False return ( self.get_expression() == value.get_expression() and self.get_variables() == value.get_variables() and self._root == value._root ) def get_expression(self): return self._expression def get_variables(self): return self._variables ... ``` This time I merged the getters in with the built-in methods only because the bulk of the code in this class isn't in the basics of the class. ```py ... def parse(self): expression = parse_expression(self.get_expression()) expression = expression["expression"] variables = expression["variables"] self._root = LogicNode(node_dict = expression) self._variables = variables self._expression = str(self._root) def make_table(self): lines = [] result = "" # Evaluate the root LogicNode evaluations = self.get_truth_values() table_dict = {} for evaluation in evaluations: if evaluation["expression"] not in table_dict: table_dict[evaluation["expression"]] = [] table_dict[evaluation["expression"]].append(evaluation["value"]) # Add column labels count = 0 length = len(table_dict) for column in table_dict: line = "| " + column.center(len(column)) if count > 0: line = " " + line elif count == length - 1: line += " |" result += line count += 1 lines.append(result) result = "" # Add label split line count = 0 for column in table_dict: line = "+" + "-".center(len(column) + 1, "-") if count > 0: line = "-" + line elif count == length - 1: line += "-+" result += line count += 1 lines.append(result) result = "" # Add truth values max_truths = -1 for column in table_dict: if max_truths == -1: max_truths = len(table_dict[column]) break for index in range(max_truths): count = 0 for column in table_dict: value = table_dict[column][index] if value: value = "T" elif value == False: value = "F" else: value = "-" line = "| " + value.center(len(column)) if count > 0: line = " " + line elif count == length - 1: line += " |" result += line count += 1 lines.append(result) result = "" return lines ... ``` Now this code may seem confusing at first but I'll break it down into what each section does. ```py # Evaluate the root LogicNode evaluations = self.get_truth_values() table_dict = {} for evaluation in evaluations: if evaluation["expression"] not in table_dict: table_dict[evaluation["expression"]] = [] table_dict[evaluation["expression"]].append(evaluation["value"]) ``` This code segment will just evaluate itself and get a list of truth values from the `LogicNode` object held in the `self._root` variable. The `get_truth_values` method will actually run all the evaluations for said object. ```py # Add column labels count = 0 length = len(table_dict) for column in table_dict: line = "| " + column.center(len(column)) if count > 0: line = " " + line elif count == length - 1: line += " |" result += line count += 1 lines.append(result) result = "" ``` This code segment will create the table labels in a truth table. For example, say your expression was `a ^ b` The resulting label would look like this: ``` | a | b | a ^ b | ``` It looks exactly as you would write a regular truth table! ```py # Add label split line count = 0 for column in table_dict: line = "+" + "-".center(len(column) + 1, "-") if count > 0: line = "-" + line elif count == length - 1: line += "-+" result += line count += 1 lines.append(result) result = "" ``` This code segment will add a line that separates the variable or expression in the label from the truth values. For example, we'll add onto the previous truth table: ``` | a | b | a ^ b | +---+---+-------+ ``` That just adds that last little line and makes it neat in raw text. ```py # Add truth values max_truths = -1 for column in table_dict: if max_truths == -1: max_truths = len(table_dict[column]) break for index in range(max_truths): count = 0 for column in table_dict: value = table_dict[column][index] if value: value = "T" elif value == False: value = "F" else: value = "-" line = "| " + value.center(len(column)) if count > 0: line = " " + line elif count == length - 1: line += " |" result += line count += 1 lines.append(result) result = "" ``` Now this next code segment has 2 parts. The first part, ```py max_truths = -1 for column in table_dict: if max_truths == -1: max_truths = len(table_dict[column]) break ``` will find the maximum amount of truth values that are given from an expression. For example, ``` | a | b | +---+---+ | T | T | | T | F | | F | T | | F | F | ``` this truth table would have 4 truth values to go through. The next part, ```py for index in range(max_truths): count = 0 for column in table_dict: value = table_dict[column][index] if value: value = "T" elif value == False: value = "F" else: value = "-" line = "| " + value.center(len(column)) if count > 0: line = " " + line elif count == length - 1: line += " |" result += line count += 1 lines.append(result) result = "" ``` will add all the other truth values to the table itself. So if you took the expression from above, `a ^ b` The final truth table would look like this: ``` | a | b | a ^ b | +---+---+-------+ | T | T | T | | T | F | F | | F | T | F | | F | F | F | ``` Now we'll move on to the `get_truth_value` and `get_truth_values` methods. This is where the height of the truth table comes from and how we manage to create every combination (not permutation) of truth values. ```py def get_truth_value(value, power): return ((value // 2 ** power) % 2) == 0 ``` This will be used in a `for` loop that iterates through 2โฟ combinations for each truth value. To simplify that, we'll use three different tables to express this. **1 variable has 2 possible truth values** ``` | a | +---+ | T | | F | ``` **2 variables have 4 possible truth values** ``` | a | b | +---+---+ | T | T | | T | F | | F | T | | F | F | ``` **3 variables have 8 possible truth values** ``` | a | b | c | +---+---+---+ | T | T | T | | T | T | F | | T | F | T | | T | F | F | | F | T | T | | F | T | F | | F | F | T | | F | F | F | ``` As you can see, we want to be able to hop back and forth between each `True` or `False` value for however many variables are in the expression. Now let's get to the code for the `get_truth_values` method. ```py ... def get_truth_values(self): # Create every possible truth combination for all variables truth_values = [] # Iterate through 2 ** variable_amount possible combinations for value in range(2 ** len(self.get_variables())): # Iterate through all variables value_dict = {} for index in range(len(self.get_variables())): # Get the power based off of the variable's index in the list power = len(self.get_variables()) - index - 1 variable = self.get_variables()[index] # Get the truth value using the get_truth_value function value_dict[variable] = get_truth_value(value, power) truth_values.append(value_dict) # Create truth values for other operations # For example, if there is a "~a", then there will be a column designated to that. # if there is a "~(a v b)", then there will be a column designated to that # as well as the "a v b" part. truth_evaluations = [] root_truth_evaluations = self._root.get_truth_values(truth_values) # Add all the truth evaluations from the root for truth_evaluation in root_truth_evaluations: if truth_evaluation not in truth_evaluations: truth_evaluations.append(truth_evaluation) # Add all the truth values as evaluations for truth_value in truth_values: for truth_variable in truth_value: truth_evaluation = { "expression": truth_variable, "truth_value": { truth_variable: truth_value[truth_variable] }, "value": truth_value[truth_variable] } truth_evaluations.append(truth_evaluation) truth_evaluations = sorted(truth_evaluations, key = lambda i: len(i["expression"])) return truth_evaluations ``` We'll go segment by segment to explain this method. ```py # Create every possible truth combination for all variables truth_values = [] # Iterate through 2 ** variable_amount possible combinations for value in range(2 ** len(self.get_variables())): # Iterate through all variables value_dict = {} for index in range(len(self.get_variables())): # Get the power based off of the variable's index in the list power = len(self.get_variables()) - index - 1 variable = self.get_variables()[index] # Get the truth value using the get_truth_value function value_dict[variable] = get_truth_value(value, power) truth_values.append(value_dict) ``` This code segment will create each dictionary for the combination of truth values given by how many variables there are. So the elements in the list that is used to evaluate expressions would match the first *n* columns where *n* is the number of different variables. So if the first row of the expression `a ^ b` would be: ``` | a | b | +---+---+ | T | T | ``` and the matching dictionary would be: ```py { "a": True, "b": True } ``` This will repeat for all the rows in the truth table. ```py # Create truth values for other operations # For example, if there is a "~a", then there will be a column designated to that. # if there is a "~(a v b)", then there will be a column designated to that # as well as the "a v b" part. truth_evaluations = [] root_truth_evaluations = self._root.get_truth_values(truth_values) # Add all the truth evaluations from the root for truth_evaluation in root_truth_evaluations: if truth_evaluation not in truth_evaluations: truth_evaluations.append(truth_evaluation) ``` As the first comment says, any variables with a ~ (NOT) operator attached to it will have their own column designated to it. This is to make it more like a truth table that you would write yourself to simplify the boolean evaluations. Again, it will only add evaluations that haven't already been evaluated. If we didn't check for that, there would be an *endless* amount of columns that exist. ```py # Add all the truth values as evaluations for truth_value in truth_values: for truth_variable in truth_value: truth_evaluation = { "expression": truth_variable, "truth_value": { truth_variable: truth_value[truth_variable] }, "value": truth_value[truth_variable] } truth_evaluations.append(truth_evaluation) truth_evaluations = sorted(truth_evaluations, key = lambda i: len(i["expression"])) ``` Now this last segment will add all the truth values for a **_single_** variable into it's own evaluation. The final line uses Python's built-in `sorted` method to sort the expressions by their lengths in order to show the build up to the final expression which is the one you want to evaluate. I know this has already been a long tutorial but just hold off for a few more explanations. The method that actually does the parsing, `parse_expression`, isn't *too* complex but it definitely requires some explanation or else you might be lost. ```py def parse_expression(expression, has_not = False): # Remove all spaces from the expression expression = expression.replace(" ", "") # Loop through and find any ^ (AND) or v (OR) operators as expressions has_not = has_not left = None operator = None right = None variables =[] parent_depth = 0 last = 0 char_has_not = False temp_has_not = False for index in range(len(expression)): char = expression[index] # Check for open parenthesis if char == "(": if parent_depth == 0: last = index + 1 parent_depth += 1 # Check for close parenthesis elif char == ")": parent_depth -= 1 # Parse expression if parenthesis depth reaches 0 if parent_depth == 0: # Check if there is a ~ (NOT) operator directly in front of the parenthesis if last - 1 > 0: if expression[last - 2] == "~": temp_has_not = True exp = parse_expression(expression[last: index], temp_has_not) if index == len(expression) - 1 and last == 0: has_not = temp_has_not temp_has_not = False # Check if there is no operator; Must be left side if operator == None: left = exp["expression"] else: right = exp["expression"] # No parenthesis depth anymore if parent_depth == 0: # Check for operator only if not within a parenthesis if char in ["^", "v"]: # Check if operator does not exist yet if operator == None: if char == "^": operator = "^" elif char == "v": operator = "v" # Operator exists; String of logical expressions exists # Make the left, operator, right into the left expression else: left = { "has_not": has_not, "left": left, "operator": operator, "right": right } if char == "^": operator = "^" elif char == "v": operator = "v" right = None has_not = False # Check for variable only if not within parentheses if ord(char) in range(ord('a'), ord('z') + 1) and ord(char) != ord('v'): # See if there is a ~ (NOT) operator directly in front of the variable if index > 0: if expression[index - 1] == "~": char_has_not = True else: char_has_not = False # Check if there is no operator; Must be left side if operator == None: left = { "has_not": char_has_not, "value": char } else: right = { "has_not": char_has_not, "value": char } char_has_not = False if char not in variables: variables.append(char) if parent_depth != 0: raise UnbalancedParentheses("You have a missing parenthesis somewhere.") variables.sort() # Check if the expression is a single expression wrapped in parentheses if operator == right == None: has_not = left["has_not"] operator = left["operator"] right = left["right"] left = left["left"] return { "expression": { "has_not": has_not, "left": left, "operator": operator, "right": right }, "variables": variables } ``` Now let's go step-by-step to explain what is going on. ```py # Remove all spaces from the expression expression = expression.replace(" ", "") # Loop through and find any ^ (AND) or v (OR) operators as expressions has_not = has_not left = None operator = None right = None variables = [] parent_depth = 0 last = 0 char_has_not = False temp_has_not = False ``` This is just the basic setup. All the variables we need, all the temporary variables we need. The reason for the `has_not` in the parameter will be explained in a little bit. ```py for index in range(len(expression)): char = expression[index] # Check for open parenthesis if char == "(": if parent_depth == 0: last = index + 1 parent_depth += 1 # Check for close parenthesis elif char == ")": parent_depth -= 1 # Parse expression if parenthesis depth reaches 0 if parent_depth == 0: # Check if there is a ~ (NOT) operator directly in front of the parenthesis if last - 1 > 0: if expression[last - 2] == "~": temp_has_not = True exp = parse_expression(expression[last: index], temp_has_not) if index == len(expression) - 1 and last == 0: has_not = temp_has_not temp_has_not = False # Check if there is no operator; Must be left side if operator == None: left = exp["expression"] else: right = exp["expression"] ``` Now the loop obviously goes through the expression as a whole which is stripped of whitespace so any spaces you put in an expression is just immediately removed. The first `if` statement, `if char == "("`, will help to keep track of the expression that is bound in parentheses. It will only match with the one that is at the same depth as it so if you have parentheses within parentheses, they will each get evaluated separately. The second `if` statement, `if char == ")"`, will determine if the current parenthesis closes off the parent parenthesis based off the `parent_depth`. If it does, then it will determine if there is a ~ (NOT) operator directly attached to it and will recursively parse through whatever is within the parenthesis. It will not include the initial set of parentheses or else that would cause infinite recursion and we don't want that. After the recursive call completes, it will reset the `has_not` variable. If we don't do that, we get incorrect expressions for specific original expressions. For example, if the original expression was `~(a ^ b) ^ c`, the parser would assume that the *entire* expression has a ~ (NOT) operator attached to it and when you printed that out, it would be `~(~(a ^ b) ^ c)`. Finally, it will determine if the parsed expression belongs to the left side or the right side based off if there is an operator or not. ```py # No parenthesis depth anymore if parent_depth == 0: # Check for operator only if not within a parenthesis if char in ["^", "v"]: # Check if operator does not exist yet if operator == None: if char == "^": operator = "^" elif char == "v": operator = "v" # Operator exists; String of logical expressions exists # Make the left, operator, right into the left expression else: left = { "has_not": has_not, "left": left, "operator": operator, "right": right } if char == "^": operator = "^" elif char == "v": operator = "v" right = None has_not = False ``` Once the parenthesis depth reaches `0`, it will find if there is an operator yet to get. The `else` statement is there in case you have an expression that doesn't include parentheses but has more than two variables to evaluate. For example, if you give the parser `a ^ b ^ c`, it will automatically put the `a ^ b` in the left side and the resulting expression will look like this: `(a ^ b) ^ c`. An easy way to get around that is by placing your own parentheses. ```py # Check for variable only if not within parentheses if ord(char) in range(ord('a'), ord('z') + 1) and ord(char) != ord('v'): # See if there is a ~ (NOT) operator directly in front of the variable if index > 0: if expression[index - 1] == "~": char_has_not = True else: char_has_not = False # Check if there is no operator; Must be left side if operator == None: left = { "has_not": char_has_not, "value": char } else: right = { "has_not": char_has_not, "value": char } char_has_not = False if char not in variables: variables.append(char) ``` This segment will only run if it is not in parentheses. What it will do is find any letter that is not the letter `v` (this is used as the OR operator) and add that to the list of variables which is used in the `parse` method mentioned earlier. It will then determine if the variable given will belong to the left or right side of the expression. Last, but not least, we have the final part of the `parse_expression` method. (yippee kayak!) ```py if parent_depth != 0: raise UnbalancedParentheses("You have a missing parenthesis somewhere.") variables.sort() # Check if the expression is a single expression wrapped in parentheses if operator == right == None: has_not = left["has_not"] operator = left["operator"] right = left["right"] left = left["left"] return { "expression": { "has_not": has_not, "left": left, "operator": operator, "right": right }, "variables": variables } ``` If the `for` loop reaches the end of the expression and the `parent_depth` is not `0`, it will obviously raise an error because you can't have an unclosed parenthesis! Then it will sort the variables in place so the truth table, when created, will show each variable in ascending order. The `if` statement there, `if operator == right == None`, is for any expression that might be enclosed in parentheses for no apparent reason *or* if the expression as a whole has a ~ (NOT) operator attached to it. For example, `~(a ^ b)` would be properly processed as the whole expression. Then obviously the `return` statement will give you the expression and all its attributes and the variables that are in the expression itself. ___ ### Other Discrete Math Operators * Implies (->) * ```py if self._operator == "->": if self.has_not(): return not (not left or right) return not left or right ``` * Biconditional (<->) * ```py if self._operator == "<->": if self.has_not(): return not (left == right) return left == right ``` * NAND (|) * ```py if self._operator == "|": if self.has_not(): return left and right return not (left and right) ``` * NOR (โฌ‡) * ```py if self._operator == "โฌ‡": if self.has_not(): return left or right return not (left or right) ``` ___ ## Final Comments I know this was a long tutorial. I did not expect it to be this long. However, I hope I helped to provide some inspiration to create a more complex logic parser. My [Logic Parser Repl](https://repl.it/@FellowHashbrown/Logic-Parser) actually has a few more operators that are in discrete math along with more operator flexibility meaning that you could type in `a and b` and it will be properly processed as `a ^ b`. ___ If you have any suggestions or see any issues, please let me know that way I can fix it! If you find any bugs in my parser, *definitely* let me know either by leaving a comment, pinging me in Repl.it's Discord Server, or pinging me in my own [Discord Server](https://discord.gg/W8yVrHt).
2
posted by FellowHashbrown (28) 1 month ago
โ–ฒ
46
3D graphics, a beginners mind.
# Preface In this tutorial I would like to show how 3d graphics is done today, why it's important, and how it will change the way you see 3d graphics applications. To better understand, we'll end up creating a 3d engine with Python. # Requirements I expect you to be familiar with Python, if you understand what `class` means you're probably qualified in this department. I also expect you to understand what the terms fov (field of view), vertex, mesh..etc. mean. # But 3d graphics is hard! No, it's not. This is an awesome area of programming you'll be able to show to your friends, there *will* be math(s) involved, so strap in, but it'll all be explained. If there are aspects you do not understand, simply copy my solution. # Fundamentals To start, let's go through the basic building blocks. Imagine we have a simple object, a cube. ![blender_2018-10-24_10-42-00](https://image.ibb.co/b5RdgV/blender-2018-10-24-10-42-00.png) There's more going on under the hood, this cube is made up of two things, *vertexes* and *triangles*. Vertexes are essentially points in 3d space. Look around your room, and imagine a speck of dust, a single point in 3d space. Triangles are, well just triangles, normal 2d flat triangles. However their three points are connected to specific vertexes. Let's look at the vertexes. ![blender_2018-10-24_10-43-32](https://image.ibb.co/dtgpoA/blender-2018-10-24-10-43-32.png) On the above image of a cube, you can see there are eight points, these are the points which make up the cube. In memory, these points each have a 3d coordinate: X, Y, Z axis. however when we go to *render* the cube, we map each 3d coordinate to 2d screen space. And the way we do that is surprisingly simple. Next, let's look at the triangles. ![blender_2018-10-24_10-43-33](https://image.ibb.co/fMqh8A/blender-2018-10-24-10-43-33.png) As you can see, a triangle is now connected to three of the points. Do this 12(*) times and you'll get a cube. *: A cube is made up of 6 faces, however to make a face with a triangle, you must use two triangles, so it ends up being 12 triangles. # Enough "fundamentals", more coding! Alright, now that we understand the basic structure for rendering 3d shapes. Let's get more technical. We'll be doing this in `Python (with Turtle)`. First, we import Turtle, I will assume you already know how to use Turtle and are familiar with it's functionality. In short, it's just a graphics library aimed at kids learning how to code with graphics, and making flowers and all sorts of things... Except we'll be going much further than flowers. ```python import turtle ``` Next we need to store our object data. We need to store all our vertexes and triangles. ```python VERTEXES = [(-1, -1, -1), ( 1, -1, -1), ( 1, 1, -1), (-1, 1, -1), (-1, -1, 1), ( 1, -1, 1), ( 1, 1, 1), (-1, 1, 1)] TRIANGLES = [(0, 1, 2)] ``` For now, we only have one triangle connected to the first three points. # Our basic main loop We want to simulate a normal graphics library with turtle. Our program will follow this structure: ```python # Create turtle, pointer = turtle.Turtle() # Turn off move time, makes drawing instant, turtle.tracer(0, 0) pointer.up() while True: # Clear screen, pointer.clear() # Draw, # ... # Update, turtle.update() ``` # Rendering Alright, now we need to somehow map these 3d vertex coordinates to 2d screen coordinates. To do this, let's use the *Perspective Formula*. Before we dive into the details of what exactly this formula does, let's start with an observation. Place an object in front of you, for instance a cup. As you move away, the cup shrinks; now this is all very obvious, but it is an essential property of 3d space we must consider. When we're creating a 3d engine, what we're doing is simulating this observation. When we move away from our objects, that is - the Z axis, we're essentially *converging* the X and Y axis toward zero. Look at this front-view of a cube, you can see the back vertexes are closer to the center (zero). ![blender_2018-10-24_19-01-34](https://image.ibb.co/kzOfvq/blender-2018-10-24-19-01-34.png) # So what is this "formula"? ```python f = field_of_view / z screen_x = x * f screen_y = y * f ``` Where x, y, z are vertex coordinates. We can simplify this to: ```python f = fov / z sx, sy = x * f, y * f ``` Easy right? So let's add `FOV` at the top of the file: ```python FOV = 100 ``` # Drawing the points Let's iterate through each vertex: ```python # Draw, for vertex in VERTEXES: # Get the X, Y, Z coords out of the vertex iterator, x, y, z = vertex # Perspective formula, f = FOV / z sx, sy = x * f, y * f # Move to and draw point, pointer.goto(sx, sy) pointer.dot(3) ``` What we get is: ![chrome_2018-10-24_19-45-21](https://image.ibb.co/kONUoA/chrome-2018-10-24-19-45-21.png) But where are our four other points from before? The ones behind? The issue is we're inside the cube, we need to move the camera out. # The camera Alright, I won't go into the camera in this tutorial, you can look at my repl at the bottom to see how to properly implement a 3d engine, but we're taking baby steps here. When we think of moving the camera, we think of the camera object moving, simple right? Well that's not easy to implement in a rasterized renderer. However what's easier is to move the *world* around it. Think about it, either you can move the camera, or move the world; it's the same effect. As it turns out, it's a lot easier to offset the vertex positions than somehow change the perspective formula to equate the position; it would be a whole lot more complex. So quickly solve this, let's move the camera out: ```python # Perspective formula, z += 5 f = FOV / z sx, sy = x * f, y * f ``` ![chrome_2018-10-24_19-52-05](https://image.ibb.co/ceSJgV/chrome-2018-10-24-19-52-05.png) And adjust the `FOV` to say, `400`. ![chrome_2018-10-24_19-53-04](https://image.ibb.co/i62jMV/chrome-2018-10-24-19-53-04.png) Nice! # Drawing triangles To draw triangles, consider this code. By this point you should be able to understand it: ```python # Draw, for triangle in TRIANGLES: points = [] for vertex in triangle: # Get the X, Y, Z coords out of the vertex iterator, x, y, z = VERTEXES[vertex] print(x, y, z) # Perspective formula, z += 5 f = FOV / z sx, sy = x * f, y * f # Add point, points.append((sx, sy)) # Draw trangle, pointer.goto(points[0][0], points[0][1]) pointer.down() pointer.goto(points[1][0], points[1][1]) pointer.goto(points[2][0], points[2][1]) pointer.goto(points[0][0], points[0][1]) pointer.up() ``` # Rotation To rotate our object, we'll be using the *Rotation Matrix*. It sounds scary, right? If you're familiar with linear algebra, you should already know this, but the rotation matrix is commonly defined as: ``` [x'] = [cos(0), -sin(0)] [y'] = [sin(0), cos(0)] ``` *using `0` as theta* I won't go into detail of the matrix. If you're unfamiliar, feel free to either research or copy & paste. To implement this, we'll first need the `math` library: ```python from math import sin, cos ``` Let's make a function to rotate: ```python def rotate(x, y, r): s, c = sin(r), cos(r) return x * c - y * s, x * s + y * c ``` Then let's place this before we do our perspective formula calculations: ```python # Rotate, x, z = rotate(x, z, 1) ``` As you can see the triangle is now rotated: ![chrome_2018-10-24_20-21-51](https://image.ibb.co/n60tFq/chrome-2018-10-24-20-21-51.png) Let's make the rest of the triangles: ```python TRIANGLES = [ (0, 1, 2), (2, 3, 0), (0, 4, 5), (5, 1, 0), (0, 4, 3), (4, 7, 3), (5, 4, 7), (7, 6, 5), (7, 6, 3), (6, 2, 3), (5, 1, 2), (2, 6, 5) ] ``` ![chrome_2018-10-24_20-23-06](https://image.ibb.co/k3Knaq/chrome-2018-10-24-20-23-06.png) Awesome! Let's initialize a counter at the start of the file: ```python counter = 0 ``` and increment this at the end of every loop: ```python # Update, turtle.update() counter += 0.025 ``` And replace our rotation function: ```python x, z = rotate(x, z, counter) ``` It's rotating, awesome! To rotate on the X, Y and Z axis: ```python x, z = rotate(x, z, counter) y, z = rotate(y, z, counter) x, y = rotate(x, y, counter) ``` We're done! # Complete code Before you read, I recommend you do read through the above, I know it's easier to just skip down to the bottom for the solutions. However, if you're here after reading through the above, feel free to post `Full read` in the comments as a token of my respect, and feel free to copy this code =) ```python from math import sin, cos import turtle VERTEXES = [(-1, -1, -1), ( 1, -1, -1), ( 1, 1, -1), (-1, 1, -1), (-1, -1, 1), ( 1, -1, 1), ( 1, 1, 1), (-1, 1, 1)] TRIANGLES = [ (0, 1, 2), (2, 3, 0), (0, 4, 5), (5, 1, 0), (0, 4, 3), (4, 7, 3), (5, 4, 7), (7, 6, 5), (7, 6, 3), (6, 2, 3), (5, 1, 2), (2, 6, 5) ] FOV = 400 # Create turtle, pointer = turtle.Turtle() # Turn off move time, makes drawing instant, turtle.tracer(0, 0) pointer.up() def rotate(x, y, r): s, c = sin(r), cos(r) return x * c - y * s, x * s + y * c counter = 0 while True: # Clear screen, pointer.clear() # Draw, for triangle in TRIANGLES: points = [] for vertex in triangle: # Get the X, Y, Z coords out of the vertex iterator, x, y, z = VERTEXES[vertex] # Rotate, x, z = rotate(x, z, counter) y, z = rotate(y, z, counter) x, y = rotate(x, y, counter) # Perspective formula, z += 5 f = FOV / z sx, sy = x * f, y * f # Add point, points.append((sx, sy)) # Draw trangle, pointer.goto(points[0][0], points[0][1]) pointer.down() pointer.goto(points[1][0], points[1][1]) pointer.goto(points[2][0], points[2][1]) pointer.goto(points[0][0], points[0][1]) pointer.up() # Update, turtle.update() counter += 0.025 ``` # Conclusion If you want to see an expanded and better written version: https://repl.it/@CoolqB/3D-Engine If there's demand I will perhaps dive into shading, lighting, culling, clipping and even texturing. If you've got any questions, fire away in the comments. Good luck!
5
posted by CoolqB (77) 5 months ago
โ–ฒ
41
How to draw Pixel Art on Python with Turtle!
# How to Draw Pixel Art on Python with Turtle! In this Tutorial you shall learn how to create your own pixel art on Python with Turtle, I hope you enjoy. The first step to creating our art is to import turtle and set a background. My personal tip is to use black as it works best with pixel art. Here is model code: ``` import turtle t=turtle.Turtle() wn=turtle.Screen() wn.bgcolor("Black") #Remember Speech Marks. ``` Then, for step 2, we will set the speed of your turtle to be a high value, using this command: ` t.speed(0) ` Then, now the technical aspects are over with, let us get into the real juicy code! First, we must define square. My optimum size after a lot of testing was a 20 by 20 square. You define a function using this piece of code: ``` def square(): for x in range(4): t.forward(20) t.right(90) #Remember Indentation. ``` Then, After that step, we can start making pixel art! To add colours to our squares, we use the begin_fill command. Let me show an example about how this works. ``` def Tetris_Piece_1(): for x in range(4): t.begin_fill() square() t.color("Blue") t.end_fill() t.color("Black") t.forward(20) ``` We created this function. Now we may uses it whenever we like with this command: ` Tetris_Piece_1(). ` This code will make a Horizontal line of 4 pixels, like the Tetris piece! Look at the example below to see Pac-Man also. You've come to the end of the Tutorial. However, if you would like to extend this, here are some ideas. Find a way to define Red_Square or Blue_Square. Make a video-game character. Create a model of a Tetris screen. It has lots of uses, so try it today. Remember to click on Python with Turtle and not python 2.7, Python or Django. I recommend you watch the example in larger screen by pressing Open in Repl.it. Special Credit to JSer for teaching me how to use markdown on this post! Up the pensize to 4 if you want it really blocky using this command! ` t.pensize(4) `
22
posted by John_WardWard (123) 5 months ago
โ–ฒ
46
A Quick Guide to Repl.it Talk Markdown
Guys! In this quick tutorial, I'll be showing you how to make your posts pretty using Markdown styling! # Headers First we'll learn about headers. A header starts with a hash symbol `#` followed by a space: ```md # A header ``` Output: # A header Headers can be a variety of sizes. A smaller header starts with more hash symbols. The number of hash symbols can be 1 to 6: ```md #### A header with 4 hash symbols ``` Output: #### A header with 4 hash symbols Alternatively, you can also "underline" a text with `=` or `-` to produce headers ```md First header (Same as # First header) -------------- Second header (Same as ## Second header) =========== ``` ___ # Text Styles You can make *italic*, **bold** or ~~strikethrough~~ text. Put the text between a pair of `*` or `_` to make it *italic*. ```md *italic text* _also italic_ ``` Output: *italic text* _also italic_ Put two `*` or `_` on both sides of text to make it **bold**. ```md **bold text** __also bold__ ``` Output: **bold text** __also bold__ You can also do a ~~strikethrough~~ by putting two tildes (`~`) on each side: ```md ~~strikethrough~~ ``` Output: ~~strikethrough~~ It's ok to mix up those stylings: ```md **_~~bold, italic and strikethrough~~_** ``` Output: **_~~bold, italic and strikethrough~~_** ___ # Lists There's two kind of lists in Markdown: **unordered** (bulleted) and **ordered** (numbered). Since repl.it talk Markdown doesn't support ordered lists (sadly), we'll only deal with unordered lists. An unordered list item starts with either a `*`, `+` or `-` followed by a space: ```md * this + that - and stuff ``` Output: * this + that - and stuff Use indentations of 2 spaces to make sublists ```md * list item * sublist item * yet another sublist item ``` Output: * list item * sublist item * yet another sublist item ___ # Links Just paste the URL and it'll work: ```md https://repl.it ``` Output: https://repl.it If you want **custom link text**, try this: `[link text](URL)`: ```md [Repl.it](https://repl.it) ``` Output: [Repl.it](https://repl.it) ___ # Images The Markdown syntax for **images** is pretty simple: `![alt text](URL)`: ```md ![Repl.it logo](https://repl.it/public/images/icon-square.png) ``` Output: ![Repl.it logo](https://repl.it/public/images/icon-square.png) **Wait... what if my image is stored in my computer? It doesn't have a URL!** Well, repl.it provided an easy way to upload images. All you need is to click the **select files** button below to upload it. After that, you'll see the Markdown code for your image in the text box. ___ # Code And finally, code!! **Inline code** and **code blocks** are widely used in repl.it talk since repl.it talk is a platform for coders to share. Wrap a pair of **backticks** (`` ` ``) around text to make a span of code (inline code): ```md `$ node index.js` ``` Output: `$ node index.js` To indicate a block of code, put three backticks (` ``` `) at both the start and end of your code: ````md ``` This is a code block. Everything here is monospaced. ``` ```` Output: ``` This is a code block. Everything here is monospaced. ```` Additionally, repl.it supports code block syntax highlighting, which is pretty useful for emphasizing readability. Just put the language name (or its short form) after the three backticks: ````md ```js while (true) console.log("MARKDOWN IS AWESOME!!!"); ``` ```` Output: ```js while (true) console.log("MARKDOWN IS AWESOME!!!"); ``` ___ # Blockquotes To do blockquotes put a `>` before each line of the block: ```md > Timchen is the greatest and > we should praise him ``` Output: > Timchen is the greatest and > we should praise him Don't forgot to leave a blank line after each blockquote! ___ # Horzontal rules A horzontal rule (a line that separates content) can be made of either three asterisks (`*`) or underscores (`_`): ```md There's a horizontal rule below *** There's a horizontal rule above ``` Output: There's a horizontal rule below *** There's a horizontal rule above That's all what I can teach in this very tutorial. Start using Markdown to style your posts, and find more about it!
14
posted by JSer (970) 5 months ago
โ–ฒ
11
Debugging in python, fun and easy!
# Debug Without Errors Debugging is the most dreaded part of programming. It should just work! And sometimes bugs can lead to hours of extra work. However they don't need to be, and by following some of the steps presented in this guide, hopefully you'll be able to fight your next bugs with vigor and valiance! In this tutorial, we'll be using [this program, which is intentionally broken](https://repl.it/@21natzil/Debug-Example). Follow along and make the changes we talk about, and see it be fixed right before your eyes! (Don't try to figure out what it does, it just does random things). Alright, so when we run this program we get... ``` The max number cannot be 0 The max number cannot be 0 The max number cannot be 0 The max number cannot be 0 ``` Oh no! We didn't even get an exception! Where do we even begin? Forchanetly, this program is very small. However for larger projects this is a huge issues. A smart place to start, would be the line that displays the output. In this case, it's ```py except: print("The max number cannot be 0") ``` Now, in this program we only want this error to be raised in the max_number is 0! Clearly, the condition that must be met to trigger this line is wrong. The condition in this scenario is a try-except case, with no defined exceptions! This is bad code 101, also define what exception you want to catch, else wise python will catch all of them. Plus, the message doesn't need to be in a try-except statement. We can create an if-statement at the start of the function, where is the max number is 0, it will print the message and exit the function. If we remove the try-except statement and add the if statement, the function will look like this: ```py def handle_math(min_number: int, max_number: int): if max_number == 0: print("The max number cannot be 0") return print( get_data() * int(min_number / max_number) ) return (min_number / max_number) ``` Great problem solved and if we run that... ``` The max number cannot be 0 The max number cannot be 0 The max number cannot be 0 The max number cannot be 0 ``` What? We just fixed this error! Why is it still here? Don't panic, we know it's not the try-catch anymore. Think about it reasonably, this means that the code calling the function is calling it wrong, so let's take a look at it. ```py numbers = [0, 1, 2, 3] for i in range(len(numbers)): r = handle_math(5, numbers[i]) numbers.insert(0, r) ``` This is a great time to use the *rubber duck* technique. The idea is you read out your code to a rubber duck, although the rubber duck is optional. So let's do just that, we make a list, create a range from 0-3, call `handle_math` with 5 and the item in the list at an index from 0-3. Then we update the items in the list by adding the result of handle_math to the start of the list. Did you catch the error? Maybe we should visualize what's happening. The list when we start looks like the this with`i` being 0. |Items:| 0 | 1 | 2 | 3 | |--------|---|---|---|---| |Index:| **0** | 1 | 2 | 3 | The `handle_math` we return None, because the value passed in was 0. Then None is added to the start of the list, and then the loop increments `i` so it's now `1`, however now the list looks like: |Items:| N | 0 | 1 | 2 | 3 | |--------|---|---|---|---|---| |Index:| 0 | **1** | 2 | 3 | 4 | As you can see, the 0 moved up, and so it's index is `1`. The *range* generator doesn't compensate for that, and so `i` will always be `0`. To fix this, instead of iterating the index, let's iterate through each element in the list, which will look like: ```py numbers = [0, 1, 2, 3] for i in numbers.copy(): r = handle_math(5, i) numbers.insert(0, r) ``` *Phew*, we're finally done! Now if we run this we'll get: ``` The max number cannot be 0 Traceback (most recent call last): File "main.py", line 22, in <module> r = handle_math(5, i) File "main.py", line 15, in handle_math get_data() * (min_number / max_number) File "main.py", line 7, in get_data return resp.content.decode('ascii') UnicodeDecodeError: 'ascii' codec can't decode byte 0x81 in position 4: ordinal not in range(128) ``` Oh no, another bug! When will it end. Don't fret, we're actually making progress, and we can locate the current error with more ease! The problem now is this error message is super complicated. `'ascii' codec can't decode byte 0x81 in position 4: ordinal not in range(128)`? What does that even mean? Errors like these are can be intimidating, but through the power of google, we will prevail! If you simply paste your exception into google, the first site is a [stackover flow post](https://stackoverflow.com/questions/21129020/how-to-fix-unicodedecodeerror-ascii-codec-cant-decode-byte) which has a response that goes into great detail. If we read that, we'll realize that the issue is we're decoding bytes into ascii, which can't represent all the values in our data. Instead, we should use uft-8, which can handle all these values. Now that function looks like: ```py def get_data(size=16): with requests.Session() as session: with session.get(f"https://httpbin.org/stream-bytes/{size}") as resp: return resp.content.decode('utf-8') ``` Wew, nice! Now that we've done our research, we should be in the clear right? Running our code now gives us: ``` The max number cannot be 0 <<<<< << < ``` Which is actually what we wanted! We're done! *I know, this looks random and trust me it is, I just needed a broken program*. I hope you learned much about debugging, and maybe I'll make a follow up using `pdb` if this is well received. [You can find the finished, debugged version here.](https://repl.it/@21natzil/Debug-Fixed) ## Have a great day!
2
posted by 21natzil (456) 1 month ago
โ–ฒ
57
Game Tutorial: Canyon Runner
Hey everyone, I made a canyon runner game (https://repl.it/@ericqweinstein/EverlastingEmbellishedDevelopments) and wrote up a tutorial for it here: https://medium.com/@eric.q.weinstein/canyon-runner-repl-it-tutorial-cdc6208d3358 Hope you like it!
26
posted by ericqweinstein (175) 7 months ago
โ–ฒ
7
Discord.py - Rewrite Tutorial using commands extension
# Discord.py Rewrite **Note : ** *Before you start this tutorial, You should already know the language **python**, **how to create applications in discord** and you should've already **invited the bot to your server**.* ------ ## Step 1 : Setting up 1. *Create a new folder for your bot, it can be anything. For reference, **I** will name it* `Sample Bot`. 2. *Open up* `command prompt` *or* `terminal` *and type the following commands in it.* ``` $ pip install -U git+https://github.com/Rapptz/[email protected]#egg=discord.py[voice] ``` 3. *Now create a new file named* `main.py` *in your bot's folder. This file will hold the main bot.* 4. *Also create a new directory named* `cogs` *in your bot's directory, this will hold our command groups (aka cogs).* ***Now you're ready to code*** ------ ## Step 2 : The main bot 1. *Open up your* `main.py` *file. Given below is some basic code with everything explained using comments.* ```python from discord.ext import commands def get_prefix(client, message): prefixes = ['=', '=='] # sets the prefixes, u can keep it as an array of only 1 item if you need only one prefix if not message.guild: prefixes = ['=='] # Only allow '==' as a prefix when in DMs, this is optional # Allow users to @mention the bot instead of using a prefix when using a command. Also optional # Do `return prefixes` if u don't want to allow mentions instead of prefix. return commands.when_mentioned_or(*prefixes)(client, message) bot = commands.Bot( # Create a new bot command_prefix=get_prefix, # Set the prefix description='A bot used for tutorial', # Set a description for the bot owner_id=374886124126208000, # Your unique User ID case_insensitive=True # Make the commands case insensitive ) # case_insensitive=True is used as the commands are case sensitive by default @bot.event async def on_ready(): # Do this when the bot is logged in print(f'Logged in as {bot.user.name} - {bot.user.id}') # Print the name and ID of the bot logged in. return # Finally, login the bot bot.run('BOT TOKEN here', bot=True, reconnect=True) ``` 2. *Now run this file, commands extension will prepare a default* `help` *command for you, you should see something similar to this when you use it: -* ![Help Command](https://i.imgur.com/8CosQsj.png) *Note that it doesn't show help when* `=help` *is used as this was done in DMs, and we only allowed* `==help` *in DMs.* 3. *Next we add the commands from the* `cogs` *directory.* 4. *Inside the* `cogs` *directory, create a new file named* `basic.py`. 5. *Add the following code above the* ```python @bot.event async def on_ready(): ``` *part in the* `main.py` *file* ```python cogs = ['cogs.basic'] ``` *This basically points to the* `basic.py` *file in the* `cogs` *folder.* 6. *Next, change* ```python async def on_ready(): print(f'Logged in as {bot.user.name} - {bot.user.id}') return ``` *to* ```python async def on_ready(): print(f'Logged in as {bot.user.name} - {bot.user.id}') for cog in cogs: bot.load_extension(cog) return ``` *This will load the all the **files** specified in the **cogs** variable.* 7. *Next, open up the* `basic.py` *file and write the following code in it.* ```python from discord.ext import commands from datetime import datetime as d # New - The Cog class must extend the commands.Cog class class Basic(commands.Cog): def __init__(self, bot): self.bot = bot # Define a new command @commands.command( name='ping', description='The ping command', aliases=['p'] ) async def ping_command(self, ctx): start = d.timestamp(d.now()) # Gets the timestamp when the command was used msg = await ctx.send(content='Pinging') # Sends a message to the user in the channel the message with the command was received. # Notifies the user that pinging has started await msg.edit(content=f'Pong!\nOne message round-trip took {( d.timestamp( d.now() ) - start ) * 1000 }ms.') # Ping completed and round-trip duration show in ms # Since it takes a while to send the messages # it will calculate how much time it takes to edit an message. # It depends usually on your internet connection speed return def setup(bot): bot.add_cog(Basic(bot)) # Adds the Basic commands to the bot # Note: The "setup" function has to be there in every cog file ``` *This will add a new ping command to your bot!* 8. Save the file ------ ## Step 3 : Checking the new command 1. *Run the **main.py** again.* 2. *Use the* `help` *command. It should look something like this: -* ![New Help](https://i.imgur.com/fschYtb.png) *Observe the new **Basic** category and **ping** command listed under it. This means that our command was successfully loaded.* 3. *Now use the* `help ping` *command, should look something like this: -* ![Ping Help](https://i.imgur.com/IUUFYGv.png) *Note that in the example, it shows the prefix used by you!* 4. *Finally, let's use the **ping command**.* ![Ping Command](https://i.imgur.com/oeGwqqf.png) *This is an example run (my internet is a bit slow)* ------ ## Step 4 : Commands with arguments 1. *Open up the* `basic.py` *file and add the following command to it by typing it just below the* `return` *statement of the ping command. This is a say command that repeats the words said by the user (in bold): -* ```python @commands.command( name='say', description='The say command', aliases=['repeat', 'parrot'], usage='<text>' ) async def say_command(self, ctx): # The 'usage' only needs to show the parameters # As the rest of the format is generated automatically # Lets see what the parameters are: - # The self is just a regular reference to the class # ctx - is the Context related to the command # For more reference - https://discordpy.readthedocs.io/en/rewrite/ext/commands/api.html#context # Next we get the message with the command in it. msg = ctx.message.content # Extracting the text sent by the user # ctx.invoked_with gives the alias used # ctx.prefix gives the prefix used while invoking the command prefix_used = ctx.prefix alias_used = ctx.invoked_with text = msg[len(prefix_used) + len(alias_used):] # Next, we check if the user actually passed some text if text == '': # User didn't specify the text await ctx.send(content='You need to specify the text!') pass else: # User specified the text. await ctx.send(content=f"**{text}**") pass return ``` 2. *Next, let's use this command!* ![Say Command](https://i.imgur.com/gRFbtl9.png) *As you can see, works perfectly!* ------ ## Step 5 : Embeds 1. *Since, we've already done this, let's do one final command. The **embed** command.* **What this command will do?** - *Ask for the title of the embed.* - *Ask the user to specify the text for the embed description.* - Set a random color to the embed. - *Send the embed in the channel* **What this will help you learn?** - *How to create and send embeds.* - *How to wait for user's response.* 2. *In your **cogs** directory, create a new file named* `embed.py` 3. *In your* `main.py` *file, to the **cogs list** which formerly was* `cogs = ['cogs.basic']` *add a new **cog** i.e.* `'cogs.embed'`*, making it -* `cogs = ['cogs.basic', 'cogs.embed']` *. This will tell it to also treat the* `embed.py` *file as another **cog**.* 4. *Open up the* **embed.py** *file and write the following code in it: -* 5. ```python from discord.ext import commands import discord import random # These color constants are taken from discord.js library colors = { 'DEFAULT': 0x000000, 'WHITE': 0xFFFFFF, 'AQUA': 0x1ABC9C, 'GREEN': 0x2ECC71, 'BLUE': 0x3498DB, 'PURPLE': 0x9B59B6, 'LUMINOUS_VIVID_PINK': 0xE91E63, 'GOLD': 0xF1C40F, 'ORANGE': 0xE67E22, 'RED': 0xE74C3C, 'GREY': 0x95A5A6, 'NAVY': 0x34495E, 'DARK_AQUA': 0x11806A, 'DARK_GREEN': 0x1F8B4C, 'DARK_BLUE': 0x206694, 'DARK_PURPLE': 0x71368A, 'DARK_VIVID_PINK': 0xAD1457, 'DARK_GOLD': 0xC27C0E, 'DARK_ORANGE': 0xA84300, 'DARK_RED': 0x992D22, 'DARK_GREY': 0x979C9F, 'DARKER_GREY': 0x7F8C8D, 'LIGHT_GREY': 0xBCC0C0, 'DARK_NAVY': 0x2C3E50, 'BLURPLE': 0x7289DA, 'GREYPLE': 0x99AAB5, 'DARK_BUT_NOT_BLACK': 0x2C2F33, 'NOT_QUITE_BLACK': 0x23272A } class Embed(commands.Cog): def __init__(self, bot): self.bot = bot @commands.command( name='embed', description='The embed command', ) async def embed_command(self, ctx): # Define a check function that validates the message received by the bot def check(ms): # Look for the message sent in the same channel where the command was used # As well as by the user who used the command. return ms.channel == ctx.message.channel and ms.author == ctx.message.author # First ask the user for the title await ctx.send(content='What would you like the title to be?') # Wait for a response and get the title msg = await self.bot.wait_for('message', check=check) title = msg.content # Set the title # Next, ask for the content await ctx.send(content='What would you like the Description to be?') msg = await self.bot.wait_for('message', check=check) desc = msg.content # Finally make the embed and send it msg = await ctx.send(content='Now generating the embed...') color_list = [c for c in colors.values()] # Convert the colors into a list # To be able to use random.choice on it embed = discord.Embed( title=title, description=desc, color=random.choice(color_list) ) # Also set the thumbnail to be the bot's pfp embed.set_thumbnail(url=self.bot.user.avatar_url) # Also set the embed author to the command user embed.set_author( name=ctx.message.author.name, icon_url=ctx.message.author.avatar_url ) await msg.edit( embed=embed, content=None ) # Editing the message # We have to specify the content to be 'None' here # Since we don't want it to stay to 'Now generating embed...' return def setup(bot): bot.add_cog(Embed(bot)) # Adds the Basic commands to the bot # Note: The "setup" function has to be there in every cog file ``` 6. *Now, let's run this command!* ![Imgur](https://i.imgur.com/PschlWI.png) 7. *And that's it!* ------ ## Step 6 : Changing the default help command 1. *As we've already seen, commands extension already comes with a built-in help command. Let's use it again!* ![Imgur](https://i.imgur.com/xM5UCOk.png) *Pretty clearly, it's very ugly. So, let's change it!* 2. *So, what we'll do is, remove the pre-defined **help** command and then create a new **help** command in the* `embed.py` *file.* 3. *Open up the **main.py** file and make the following changes: -* ```python @bot.event async def on_ready(): print(f'Logged in as {bot.user.name} - {bot.user.id}') bot.remove_command('help') # Removes the help command # Make sure to do this before loading the cogs for cog in cogs: bot.load_extension(cog) return ``` 4. *Open the* `embed.py` *file and make a new **help** command is given below: -* **NOTE : ** *We're making this in the **embed.py** file because this help command will be using embeds too. ( though it's not necessary to do even if it uses embeds, I just don't want to create another file with all those colors).* **What this help command will do?** - Get all the cogs registered. - If the user has passed a cog argument, then list all commands under that cog. - Else, list all cogs and commands under them. ```python @commands.command( name='help', description='The help command!', aliases=['commands', 'command'], usage='cog' ) async def help_command(self, ctx, cog='all'): # The third parameter comes into play when # only one word argument has to be passed by the user # Prepare the embed color_list = [c for c in colors.values()] help_embed = discord.Embed( title='Help', color=random.choice(color_list) ) help_embed.set_thumbnail(url=self.bot.user.avatar_url) help_embed.set_footer( text=f'Requested by {ctx.message.author.name}', icon_url=self.bot.user.avatar_url ) # Get a list of all cogs cogs = [c for c in self.bot.cogs.keys()] # If cog is not specified by the user, we list all cogs and commands if cog == 'all': for cog in cogs: # Get a list of all commands under each cog cog_commands = self.bot.get_cog(cog).get_commands() commands_list = '' for comm in cog_commands: commands_list += f'**{comm.name}** - *{comm.description}*\n' # Add the cog's details to the embed. help_embed.add_field( name=cog, value=commands_list, inline=False ).add_field( name='\u200b', value='\u200b', inline=False ) # Also added a blank field '\u200b' is a whitespace character. pass else: # If the cog was specified lower_cogs = [c.lower() for c in cogs] # If the cog actually exists. if cog.lower() in lower_cogs: # Get a list of all commands in the specified cog commands_list = self.bot.get_cog_commands(cogs[ lower_cogs.index(cog.lower()) ]) help_text='' # Add details of each command to the help text # Command Name # Description # [Aliases] # # Format for command in commands_list: help_text += f'```{command.name}```\n' \ f'**{command.description}**\n\n' # Also add aliases, if there are any if len(command.aliases) > 0: help_text += f'**Aliases :** `{"`, `".join(command.aliases)}`\n\n\n' else: # Add a newline character to keep it pretty # That IS the whole purpose of custom help help_text += '\n' # Finally the format help_text += f'Format: `@{self.bot.user.name}#{self.bot.user.discriminator}' \ f' {command.name} {command.usage if command.usage is not None else ""}`\n\n\n\n' help_embed.description = help_text else: # Notify the user of invalid cog and finish the command await ctx.send('Invalid cog specified.\nUse `help` command to list all cogs.') return await ctx.send(embed=help_embed) return ``` 5. *This is just an example and gives you an basic idea of what it is like. Below is a sample run of this new help command.* ![Main Help](https://i.imgur.com/q0mCFDb.png) *No **cog** specified, commands listed* ![Embed help](https://i.imgur.com/SEAZ2Cf.png) ***Embed cog*** *specified, show the details of commands under **Embed** category.* ![Invalid help](https://i.imgur.com/lITNGie.png) *Invalid **cog** specified, send error message.* ------ ### Quick Tip *Use `.env` files to store your token. I've used .env file to store my token in the final code. You can learn more about how to use `.env` files [here](https://repl.it/site/docs/repls/secret-keys).* ------ ### The End ***Those are all the basics you'll need to know, refer to [API Reference](https://discordpy.readthedocs.io/en/rewrite/ext/commands/api.html) for reference on the discord.py commands extension. You can also refer to the [main library docs](https://discordpy.readthedocs.io/en/rewrite/api.html). All of the above code can be obtained at [here](https://repl.it/@TheDrone7/discordpy-rewrite). Also you can visit [this link](https://repl.it/talk/learn/Hosting-discordpy-bots-with-replit/11008) to learn how to host your discord bots on [repl.it](https://repl.it/)!***
9
posted by TheDrone7 (101) 1 month ago
โ–ฒ
13
Introduction to Machine Learning with Python and Repl.it
Hey all, I've written a tutorial to explain basic machine learning concepts and to show how to get started with the great Python scikit-learn library. I hope it helps, especially if you're taking part or wanting to take part in the Repl.it AI competition! The tutorial is published over here: https://www.codementor.io/garethdwyer/introduction-to-machine-learning-with-python-and-repl-it-rln7ywkhc As always, keep the feedback coming!
0
posted by GarethDwyer1 (88) 2 months ago
โ–ฒ
22
How to make Rest Api in Python
## Introduction In today's lesson, we will learn how to build basic `Rest Api` in `Python` and `Flask`. We will specifically focus on two different way of creating apis, both will be using flask. List of two ways. 1. using flask 2. using flask extension called `flask restful` In this Lesson we are going to use `flask restful` to make our final api. But I'm also going to show to how to create one in `flask > `Note!` using flask is not the most official way of creating api. Flask is not efficient, code will look bad and have difficulty managing large files. > `flask restful` flask extension restful is the best way of creating api. Because it handlea big files wasily. Very easy to work with And it was created special for making rest apis. > I recommend you should use `flask restful` >`Here what our final api will look like` ![Api final Result](https://cdn.discordapp.com/attachments/485343377983012864/518852699572273182/restapi_final.gif) It will generate random content from a category and print it out in json format ## What is REST? Representational State Transfer (REST) is a software architectural style that defines a set of constraints to be used for creating web services. Web services that conform to the REST architectural style, termed RESTful web services, provide interoperability between computer systems on the Internet. RESTful web services allow the requesting systems to access and manipulate textual representations of web resources by using a uniform and predefined set of stateless operations. Other kinds of web services, such as SOAP web services, expose their own arbitrary sets of operations. In a RESTful web service, requests made to a resource's URI will elicit a response with a payload formatted in either HTML, XML, JSON, or some other format. For more info [click here](https://en.wikipedia.org/wiki/Representational_state_transfer) > **We will only work with json format** ## Requirements - Basic Python knowledge - Flask - Flask-Restful - Jsonify - json ## Installation Inside your repl, creating an empty file called `requirements.txt`. Once you have to empty txt file ready, copy and paste this **Flask==1.0.2** to your **requirements.txt** to install flask. ![Installation Successful](https://cdn.discordapp.com/attachments/487306993288347649/518847564968361984/unknown.png) If this started to happen, that mean flask have been install, and if not, something is wrong and should re-copy and paste `Flask==1.0.2` To install flask-restful, repeat the same procedure but use this line `Flask-RESTful==0.3.6` ## Creating web server We'll need to create basic web server. We need web server to run our code on [repl.it](https://repl.it/repls) here the basic code for creating basic web server ```python from flask import Flask from threading import Thread app = Flask('') @app.route('/') def home(): return "I'm alive" def run(): app.run(host='0.0.0.0',port=8080) t = Thread(target=run) t.start() ``` >I'm not going to explain this code, as I'm bad when it come to server stuff. But up there we just created a simple server. if you run this code this screen is going to pop up on top right hand right with this text `i'm alive`. Now we have successfully created a web server. ## Rest REST have 4 methods: - GET - PUT - POST - DELETE In this tutorial, we ony going to work with `GET` method. ## Creating api first we need to import another flask extension `jsonify`. ```python from flask import jsonify ``` >we need jsonify to convert data in json and send it out to our server. >Important Note:- we're going only going to work with json data, because json can be used in almost every modern language. ```python from flask import Flask, jsonify from threading import Thread app = Flask('') #make sure you code is between this code t = Thread(target=run) t.start() ``` here's a example of **GET** methods ```python from flask import Flask, jsonify from threading import Thread app = Flask('') @app.route('/') def home(): return "I'm alive" @app.route('/api/add', methods=['GET']) def add(): return jsonify({"2 + 2": 2 + 2}) def run(): app.run(host='0.0.0.0',port=7000) t = Thread(target=run) t.start() ``` ```python @app.route('/api/add', methods=['GET']) ``` **/api/add** is our api endpoint. you can name it what ever you want, im just gonna called it that. we need an endpoint to get to make requests to content, without any endpoint it will give you an error or simply return you home page if available. You also noticed that we in our **@app.route**. we have **methods**. We use methods to tell what kind of `rest option`, we are using. In this case we are `GET`. **ENDPOINT**= your web server url + your app route so my url is `https://rest-api-python.badvillain01.repl.co` + route `/api/add` our endpoint is `https://rest-api-python.badvillain01.repl.co/api/add` > **Note**:- your url name will be different than mine. so put your url name and route together. If you run my example you will get this result. `{"2 + 2":4}` > I recommend running my example first and once you have hold of it, then run you're owns. Now in this example we will take `user input` and convert this to json data. I'm going to use to previous example but add user input. So user will put any number and it will double user input. ```python @app.route('/api/add/<int:num>', methods=['GET']) def add(num): return jsonify({f"{num} + {num}" : num + num}) ``` You may have noticed that our in route we have this `<int:num>`. This is how to take input. `int` is specify what type of content is it. And `num` is the name of variable we will storing our input. Once we have our input. Then we are going to use `jsonify` to convert data into json format and print it out on server. If you run this code now and endpoint `/api/add/<any num you want>` i'll be using 23, json data should look something like that. ```py {"23 + 23" = 46} ``` Now he's another example that takes string as input ```py @app.route('/api/name/<string:name>', methods=['GET']) def get_name(name): return jsonify({"Your name": name}) ``` we bascially did same thing, just change our variable type to string. and if we run this example result show look like this ```json {"Your name": "bad"} ``` _That all i have for flask. If you wanna continued with flask, **Good Luck**, but i'll suggest you checkout `flask-restful`_ # Getting ready for flask-restful Api So, the api i'm creating have two main categories, `facts` and `quote`. And then inside the folder, there are four `json` files, that contain some sort of json content. _I hope this made any sense to you. If not, then i'm sorry_ ![v](https://cdn.discordapp.com/attachments/485343377983012864/518870277812256771/unknown.png) # flask-restful first we need to import two extension from `flask-restful`, We need `Resource`, and `Api`. ```py from flask_restful import Resource, Api ``` Here a simple example of **flask-restful** ```py from flask import Flask, jsonify from threading import Thread from flask_restful import Resource, Api app = Flask('') api = Api(app) class Test(Resource): def get(self): return "Example with Flask-Restful" #creating api endpoint api.add_resource(Test, '/api/restful') def run(): app.run(host='0.0.0.0',port=7210) t = Thread(target=run) t.start() ``` If you compare this with **flask**. You can clearly see this is more readable, official and best. #### How flask-restful works first we need to build `api` on top of `app`. ```py app = Flask('') api = Api(app) ``` We just creating `api`. Now we dont need `app`. And we are going to use `api` to add new content. ```py class Test(Resource): def get(self): return "Example with Flask-Restful" ``` > In `flask-restful`, all content need to be in class with `Resource` as parameters > **Methods** are a little different. You don't assign Methods in route endpoint, instead you add methods directly to class. `def get(self):` or `def post(self)` > Self: becuase we are using `def` inside class, so we need to add `self` as first parameter. To create `endpoint` in flask-restful, it's pretty easy. `api.add_resource(<your class name>, <your endpoint>)` For this example `api.add_resource(Test, '/api/restful')` If you run this now. You should get this `"Example with Flask-Restful"` **NOTE** ```py def get(self): return "Example with Flask-Restful" ``` As you see, we didn't use `jsonify`. **WHY**. Becuase the content we're returning in not `json`. So to return `json` data. Here's an example ```py def get(self): return jsonify({"Type": "flask-restful"}) ``` Output should be `{"Type":"flask-restful"}` ## Creating final Api Becuase i'm gonna be selecting random from json fromat, so i need to import `random` and also need to import `json` ```py import random import json ``` Now we are going to create a class called `Facts`. This class will return a random facts. ```py def get_facts(fact_type): if fact_type == "random": file_address = 'Facts/random.json' elif fact_type == "technology": file_address = 'Facts/technology.json' else: file_address = 'errormsg.json' with open(file_address, 'r') as factfile: data = json.load(factfile) fact = random.choice(list(data['Facts'])) return fact class Facts(Resource): def get(self, fact_type): return get_facts(fact_type) api.add_resource(Facts, '/api/facts/<string:fact_type>') ``` So what i did that, instead of adding everything to my `Facts` class, i created a new def, outside of class. Now everythime, I'm calling Facts endpoint. It's sending requests back to `get_facts()` and return the data to `def get()`. And then this whole return the data to our server. > Creating new separate data will make your code more readable. you may wonder what this for ```py def get_facts(fact_type): if fact_type == "random": file_address = 'Facts/random.json' elif fact_type == "technology": file_address = 'Facts/technology.json' else: file_address = 'errormsg.json' with open(file_address, 'r') as factfile: data = json.load(factfile) fact = random.choice(list(data['Facts'])) return fact ``` > **fact_type** is user input. So i only want user to choose from my inputs, so im using `if` statement to check user input. If user input is equal to one of my inputs, I'm creating a new variable called `file_address`. This will contain file address i want to open. ```py with open(file_address, 'r') as factfile: data = json.load(factfile) fact = random.choice(list(data['Facts'])) ``` now once `if else` are done. We need to open the file and select random items from it. Remember, we are storing file address in `file_address`. We are going to called this file `factfile` Once we open the file, we need to load all content to `json` file. `data = json.load(factfile)` and now open we have all content in json file, we need to random select one. `fact = random.choice(list(data['Facts']))` > data:- name of variable which contain our json content > data['Facts]: `Facts` is what we want from our json file. It will randomly select one thing from `Facts` and return to `def get_fact` and this will return everything to `class Facts` and this will return it to our server. Now if you run your code, the output should look like this ![Facts Results](https://cdn.discordapp.com/attachments/485343377983012864/518911100968763392/ezgifcomvideocutter.gif) Now we are going to do the same thing with `Quotes`. We'll create a class and remember to give `Resource` parameter to class. ```py def get_quotes(quote_type): if quote_type == "motivation": file_address = 'Quotes/motivation.json' elif quote_type == "funny": file_address = 'Quotes/funny.json' else: file_address = 'errormsg.json' with open(file_address, 'r') as quotefile: data = json.load(quotefile) quote = random.choice(list(data['Quotes'])) return quote class Quotes(Resource): def get(self, quote_type): return get_quotes(quote_type) api.add_resource(Quotes, '/api/quotes/<string:quote_type>') ``` So we are just doing the same thing we did previously. **Congrats**, you have successfully created your first rest api. >**I hope you learned something today** >**I know it's bad but i try my best to make a great tutorial** _If you have any question dm me on discord, ask in **repl.it official discord server** or comment below_ > If you see any error or mistake. Please notify me. > **Important Note**:- your repl web server won't stay up 24/7. I will die after 60 min. So i suggest using free service `UptimeRobot`. If you never used `UptimeRobot`, then read [this tutorial for help](https://repl.it/talk/learn/How-to-use-and-setup-UptimeRobot/9003) This api was just for tutorial. I'm not working on it anymore. [Please check out of my main api.](https://gold-miners-api.badvillain01.repl.co/docs.html) ### Source code https://repl.it/@badvillain01/rest-api-python
13
posted by badvillain01 (50) 4 months ago
โ–ฒ
29
Game Tutorial: SSSnake!
Hi everyone, I put together a little [Snake](https://repl.it/@ericqweinstein/SSSnake) game based on [the arcade game _Blockade_](https://en.wikipedia.org/wiki/Snake_(video_game_genre)) (created in 1976!) and thought I'd write a tutorial on how the game works. (This code is based on work by [Ahira Patel](https://github.com/ahirapatel/python-snake-cli).) Feel free to fork the REPL and add to it! The code is broken up into four main files: `board.py`, `constants.py`, `snake.py`, and `main.py`. The first file manages drawing the game board, `constants.py` sets up nice [enums](https://en.wikipedia.org/wiki/Enumerated_type) for our board colors and symbols, `snake.py` handles drawing the snake itself, and the last file, `main.py`, ties everything together to run our game. ## `constants.py` Since `constants.py` is the simplest, we'll start there. The entire file looks like this: ```py from enum import Enum class Colors(Enum): ''' Terminal colors. ''' BLUEBACK = '\x1b[44m' END = '\033[0m' GREEN = '\033[92m' RED = '\033[91m' class Symbols(Enum): ''' Symbols used during gameplay. ''' SNAKE = f'{Colors.GREEN.value}o{Colors.END.value}' EMPTY = ' ' GRID = '.' FOOD = f'{Colors.RED.value}*{Colors.END.value}' WALL = f'{Colors.BLUEBACK.value}{Colors.GREEN.value}|{Colors.END.value}' ``` All we're doing is setting up some nicer syntax so we can use `Colors.RED.value` or `Symbols.FOOD.value` throughout our code. This also makes it easier for us to change things throughout our program by centralizing logic in one placeโ€”for example, we can change the "food" symbol from `*` to `+` by just changing one character. ## `board.py` Next, let's take a look at `board.py`. This file is a little longer, so we won't reproduce it here, but feel free to follow along in the REPL as we move from function to function. All our functions are inside a `Board` class, which keeps track of the symbols we use in the terminal for the various pieces of our board (such as walls and empty squares). (Note that functions inside classes are called _methods_, so we'll call them that throughout this tutorial.) We also do some arithmetic to build the board from rows and columns, which are in turn dictated by the dimensions of our terminal. (Feel free to play around with different terminal sizes! You can find these values in the `get_terminal_dimensions` function in `main.py`.) Next, we define [getters and setters](https://en.wikipedia.org/wiki/Mutator_method), `get` and `set`. The `get` method gives us the symbol associated with a particular pair of terminal coordinates (that is, what symbol is located at a particular row and column), and the `set` method lets us change the symbol located at a set of coordinates. We also define `width` and `height` to give us the number of columns and rows, respectively, as well as an `is_valid_coord` method that checks if a proposed row/column coordinate pair is within the bounds of our terminal. Our last two methods handle drawing the board itself. The `draw_initial_board` method does exactly that: it goes through and draws the board row-by-row (including the outer walls). Finally, the `draw` method goes to the provided coordinates on the board and draws the provided symbol (for example, drawing the food symbol at a random spot on the board). Note that both methods use `sys.stdout.flush()` to ensure the changes we've made are written to the screen (since content we write to the terminal is buffered; you can read more about data buffers in [here](http://en.wikipedia.org/wiki/Data_buffer)). You can think of `write`ing as telling the program how the terminal content is going to change, and `flush`ing makes those changes appear in the terminal. ## `snake.py` Moving on, we get to the `Snake` class in our `snake.py` file. In our `__init__` method, we keep track of the snake's current direction, head, tail, and what the snake has eaten, as well as a bit of bookkeeping to help us draw the snake. (A discussion of threading in Python is beyond the scope of this tutorial, but you can Google "Python threading" to find some more information on the topic, such as [this WikiBook](https://en.wikibooks.org/wiki/Python_Programming/Threading).) Most of our snake's functionality can be found in the `move` method, which controls the snake's movement across the board: we acquire the lock (ensuring no other threads can change the game state while we're in the middle of moving), move the head to its new position, remove the tail, and then signal that we're done moving by updating our `movement_processed` flag and releasing the thread lock. The result? Our snake moves one cell at a time across the board! The `consume` method handles the snake's behavior when it eats something: when it successfully eats the food symbol, we grow the snake by one unit. The `is_hungry` method helps us figure out whether the snake has recently eaten any food, and `is_dead` handles the two conditions that make us lose the game: if we "eat" a wall or "eat" our own tail (that is, collide with a wall or ourselves). The `set_movement` method allows the snake to change direction (note that it prevents a snake that's more than one unit long from "reversing"โ€”that is, the snake can't immediately go down while it's going up, left while it's going right, and so on). Last but not least, the `get_head` and `get_old_tail` methods are helpers that make it easier for us to get ahold of the snake's head and tail while updating its state on the board. ## `main.py` Finally, we'll tie everything together in a `Game` class inside our `main.py` file. Most of our game logic lives here, so we'll spend a bit more time unpacking this code. First, we `__init__`ialize our class with a board (`self.board`), how many pieces of food the player has to eat to win (`self.num_food`), and the snake (`self.snake`), as well as some bookkeeping to keep track of whether the game is over (`self.over`) and, if so, how the player signalled they wanted to quit the game (`self.key_quit`, `self.sig_quit`). Next, we have our `play` method, which `sleep`s for two seconds to give the player time to switch over to the REPL, then does the following inside a loop: * Ends the game as needed (if the player crashes into a wall/themselves, eats all the food, or quits); * Updates the game board state; * Draws the updated board on the terminal; * Briefly sleeps to allow the user time to react. Next, the `spawn_new_food` method does what you'd expect: it generates a random coordinate, tries to place the food at that spot, and retries if it's not successful (for example, if the snake is already occupying that spot on the board). The `draw_game_board` and `update_game_board` are also fairly straightforward: the first method updates the board (and the snake as well, as long as it's still hungry), and the second updates the game state by moving the snake and updating the board, allowing the snake to consume whatever's in the current square (growing the snake if it eats food and ending the game if the snake bites the wall or itself). Outside of our `Game` class, we have a handful of helper functions that allow us to handle user input and nicely transition the terminal from "game mode" back to the regular REPL (and vice-versa). These can be a little confusing, since they have more to do with the particulars of the terminal than the mechanics of our game, so we'll go over them one by one: * `movement_listener` listens for user input and sets the snake's direction via `snake.set_movement()`. (It also does some nice things to ensure the terminal looks good; these are described in the comments.) * `get_terminal_dimensions` is just a nice helper function for getting the height and width of our terminal. (We actually do some subtraction to make the dimensions slightly smaller than the actual terminal, in order to ensure the entire game is visible on-screen.) * `start_alternate_screen` and `end_alternate_screen` are pretty much as-advertised: they handle the switch to the terminal game from the REPL and back again. * `exit_as_needed` is also pretty straightforward: it handles the four ways our game can end (the user loses by crashing the snake, the user wins by getting all the food, or the user quits the game by either explicitly quitting or stopping the game by sending `Ctrl-C`). * `signal_handler` is a callback function we provide to `signal.signal` to tell Python what to do if it gets an interrupt (`SIGINT`) or terminate (`SIGTERM`) signal from the user: in either case, we quit the game. * Finally, `quit` sets `game.over` to `True` and cleans up the game terminal, restoring our settings to the way they were before the game started. The last few lines of code in `main.py` set up our game for us by getting the terminal dimensions, showing a countdown timer for the game to begin, handle the switch from the "regular" REPL to the game (lines 181 to 183), and finally, creating a new game and starting it. ...and that's it! I hope you enjoyed this tutorial, and feel free to [fork the REPL](https://repl.it/@ericqweinstein/SSSnake) and add more to the game.
0
posted by ericqweinstein (175) 4 months ago
โ–ฒ
38
Python Advanced Concepts Explanation (for beginners)
(just wrote this up in a few minutes, so there might be typos and stuff) (the reader is expected to have at least basic knowledge of python) (and programming in general) (if not, read [fullern's introduction][0]) (if you have a question about it (or feedback), comment here or join the repl.it discord and DM elias#7990) This document is an explanation of how Python handles and manipulates data. It is an attempt to clarify some common misconceptions about Python. Important vocabulary and facts will be **bolded**. First, forget what you know about Python objects (but not syntax). I will be explaining most of the useful features here. ## Expressions: Expressions in python are the main way of manipulating data. They calculate (**evaluate**) a value based on the current state of the program. There are a few types of expressions: - Literals: Literals always evaluate to the same thing. For example, the literal `5` always evaluates to the number `5`. - Operations: Operations combine values. For example, the `+` operations adds numbers (or concatenates strings). An expression with an operation looks like `<some expression> <operation (+, -, *, etc.)> <other expression>` (without the angle brackets). Operations follow an extended version of PEMDAS, described [here][1]. - Function calls: These are described in the Functions section. - Ternaries: Ternary expressions look like `<expression a> if <condition expression> else <expression b>`. First `<condition expression>` is evaluated, and if it is **truthy** (acts like True; see Truthiness section for more), `<expression a>` is evaluated. If it is not truthy (**falsy**), `<expression b>` is evaluated instead. - Comprehensions: See the Comprehension section ## Code structure: Python code is composed of lines and blocks. Lines in python can have a few different forms (more than the ones shown here, but the others are used less): ```python <expression> # <expression> is evaluated, and then the value is discarded variable = <expression> # <expression> is evaluated, and assigned to variable as described in Scoping. var1, var2 = <expression> # <expression> is evaluated, and (assuming it's a list, tuple, etc.; see Iterables) var1 is set to its zeroth element and # var2 is set to its first (var1, var2), var3 = <expression> # <expression> is evaluated, and (var1, var2) is set to its zeroth element and var3 is set to its first # other patterns similar to those above also work, like (var1, var2), (var3, var4) # i use <lvalue> to represent any of those patterns if <expression>: # <expression> is evaluated <more lines of code, indented by some amount> # if <expression> was truthy, <more lines of code, indented> is run and the code skips to <more lines, not indented> elif <expression2>: # <expression2> is evaluated (only if <expression> was falsy). elifs are optional, and don't have to be included <more indented code> # if <expression2> was truthy, <more indented code> is run and the code skips. ... # the pattern continues else: <last indented code> # if none of <expression>, <expression2>, ... was truthy, <last indented code> is run. <more lines, not indented> while <expression>: # evaluate <expression>. if it is true, <indented code> # run <indented code> and go back to the start for <lvalue> in <expression>: # for every element in <expression> (probably a list, tuple, or iterable), set <lvalue> to that element and <indented code> # run <indented code> def name(value): # described in Functions <indented code> class name(): # described in User-Defined Classes <indented code> ``` `if`, `while`, `for`, `def`, and `class` can also be used in indented code. ## Scoping: When a program starts running, all variables are in a **scope** (collection of variables) called the **global scope**. Other scopes can be created and manipulated as described in Functions and User-Defined Classes. ## Objects & Types: Data in Python is an object. Numbers are **objects**, string are objects, and functions are objects. Statements like `if`, `for`, and `while` are not objects, since they are code, not data. Every object in Python has a **type**, a.k.a. class. The type of a number is `int` or `float`. The type of a string is `str`. The type of an object can be determined with `type(obj)`. These types are also objects. Their type is called `type`. `type`'s type is itself. Types (but not non-type objects) also have superclasses. These superclasses are also types, and specify additional data about the type (more info in Attributes). If a type has multiple superclasses, [this algorithm][2] is used to determine priorities for them in certain situations. ## Attributes: Objects also have **attributes**. These attributes are named. Their names start with a character a-z, A-Z, or \_. The characters after the first can be a-z, A-Z, \_, or a digit. For example, an attribute could be called `an_attribute`, `AbC123`, or `__add__`, but not `123AbC` (the first character cannot be a digit). These attributes can be accessed with `the_object.attribute_name`. When an attribute is accessed, if it is not found, the object's class (a.k.a. type) is checked. If the attribute is not present there, the superclasses of the type are checked next, in order by priority. If none of the superclasses have the attribute, an exception is raised (see Exceptions). Attributes also have **values**, which are the values returned when an attribute is accessed. These values can be any object. There are also **descriptors**, which allow code to be run when an attribute is accessed. They are described in more detail in the Descriptors section. Some attribute names are special. For example, an attribute named `__add__` specifies how objects are added together with `+`. This will be described in more detail in the User-Defined Classes section. ## Functions: **Functions** are snippets of Python code, and are also objects. They can be **called** with `the_function_object(argument_1, argument_2, ...)`. When a function is called, the code that called it is paused, the passed arguments are placed in variables, and the function's code is run. When the function executes a `return` statement, the function's code stops, and the returned value is send back to the calling code. If the function reaches the last line, it returns `None`, an object representing no data. For example, ```python def a_function(number): # when a_function(value) is called, the calling code is paused # then a new scope is created, # and a variable called number is created with the argument in it # the function's code is then run in the new scope result = number + 1 # result is created in this function's scope return result # the function's code stops here, and result is sent to the calling code. function_result = a_function(41) # calling a_function, where argument is set to 41 # function_result is set to the value returned from a_function print(function_result) # print is a function too! it returns None ``` When run, this code takes the following path: 1: ```python def a_function(number): # all that code above ``` This line (and the code in the `def`) creates a function object, then puts it in a variable called `a_function`. 2: ```python function_result = a_function(41) ``` This line calls `a_function` with the argument `41`. We'll come back to this line once the function returns. 3: ```python def a_function(number): ``` The function is called, so a new scope is created. Here, the argument `41` corresponds to the variable `number`, so the variable `number` in the new scope is set to `41`. 4: ```python result = number + 1 ``` This line calculates `41` (the value of the `number` variable) `+ 1`, which equals `42`. The `result` variable is then set to `42`. 5: ```python return result ``` This line ends the function, returning the value of `result` (`42`). You don't need to return a variable, so this function could have just returned `number + 1` instead. This also goes back to the scope that existed before the function was called (the global scope). 6: ```python function_result = a_function(41) ``` Now that the function has returned, `function_result` is set to the value it returned, which is `42`. Note that this variable is created in the global scope, not in `a_function`'s scope. 7: ```python print(function_result) ``` This outputs the value of `function_result`, or `42`. ## Generators, Iterators, and Iterables **Generators** are one of my favorite features in Python. They are easiest to understand with an example, so here is one: ``` def a_generator_func(): print("about to yield first value") yield "first value" print("just yielded first value") print("about to yield second value") yield "second_value" print("done yielding, there is no third value") a_generator = a_generator_func() print("about to get first value") print("the first value is", repr(next(a_generator))) print("about to get second value") print("the first value is", repr(next(a_generator))) print("about to get third value") print("the first value is", repr(next(a_generator))) ``` This code will output ``` about to get first value about to yield first value the first value is 'first value' about to get second value just yielded first value about to yield second value the first value is 'second_value' about to get third value done yielding, there is no third value Traceback (most recent call last): File "python", line 17, in <module> StopIteration ``` `a_generator_func` is a function, that, instead of pausing the main program when called, returns an object representing the current state. `next` can be repeatedly called on that object to run it until it `yield`s a value. When a generator runs out of things to yield, a `StopIteration` exception (TODO: section on exceptions) is raised. These can be caught with: ``` try: next_val = next(a_generator) except StopIteration: print("the generator ended") ``` Generators can also be used with `for`: ``` for elem in a_generator_func(): print(elem) ``` This would output ``` about to yield first value elem: first value just yielded first value about to yield second value elem: second_value done yielding ``` , but would not raise `StopIteration` (`for` automatically handles this). Generators are a special class of **iterators**, which are objects that can have `next` called on them, and can be used with `for`. `list`s and `tuple`s are not iterators, but **iterables**, which can be converted to iterators by calling `iter` on them. ## User-Defined Classes There are many online tutorials about creating classes, most of which are better than I can write. So before you read this, go look at one of those tutorials (but keep in mind that everything in play is an object). So, now that you have read one of those tutorials (go read one if you haven't), consider the following example class: ```python class Point(): def __init__(self, x_pos, y_pos): self.x_pos = x_pos self.y_pos = y_pos def print(not_self): print(not_self.x_pos, not_self.y_pos) def __add__(tomayto, tomahto): return Point(tomayto.x_pos + tomahto.x_pos, tomayto.y_pos + tomahto.y_pos) a = Point(1, 2) b = Point(3, 4) a.print() b.print() (a + b).print() ``` This code creates a `Point` class, and 2 `instances` of it (`a` and `b`). It then calls `.print` on both instances and `a + b`. The only peculiarity you might notices about this is that it doesn't use `self`, but different argument names. Despite what most tutorials would have you believe, there is nothing special or magic about `self`. You can replace it with any other name and your code will work fine. If your tutorial didn't cover `__add__` and similar functions, they basically define what happens when `a + b` is executed. More information can be found [here][3]. The only "magic" thing left in this code is how the `self`/`not_self`/`tomayto` arguments actually get added; `a.print()` never specifies a value for `not_self`. This is covered in the next section, ## Descriptors Descriptors are a way to make code run when an attribute is accessed. They are objects with `__get__` or `__set__` special methods. When they are accessed as an attribute of a class (but not from an instance), the `__get__` method is called with the instance and the class the descriptor was found on. When attributes are set, a similar thing is done with `__set__`. This is how functions automatically add a `self` argument. They are descriptors, so when they are accessed, it returns a "bound" version of the function, that has the instance (`self`) already added as an argument. A shorthand way to create descriptors is: ```python class Test(): @property def x(): print("getting x") @x.setter def x(): print("setting x") a = Test() a.x a.x = 1 ``` The `@property` and `@x.setter` lines are decorators, described in the next section. ## Decorators Consider the following code: ```python def function_making_function(old_function): print("making a new function from", old_function) def new_function(): print("calling the old function") old_function() print("done calling the old function") print("made the new function") return new_function ``` This code is relatively simple, except for the fact that a function is defined inside another function. This gives the inner defined function access to all the variable in the outer function. Now, running ```python def some_function(): print("in some_function") print("starting to make a new function") new_function = function_making_function(some_function) # not calling it here, just moving at around (remember, functions are objects!) print("done making the new function") print("calling the new function") new_function() print("done") ``` will output: ``` starting to make a new function making a new function from <function old_function at 0xhesyfyua> made the new function done making the new function calling the new function calling the old function in some_function done calling the old function done ``` The code ```python9o print("starting to make a new function") @function_making_function def some_function(): print("in some_function") print("done making the new function") print("calling the new function") some_function() print("done") ``` does the exact same thing as the other code above. `@function_making_function` is just shorthand for `some_function = function_making_function(some_function)` (but it must be before the `def`). This is called a decorator. Multiple decorators can be used on the same function, and are applied bottom-to-top. Decorators do not have to return functions; they can also return other objects (such as descriptors in the case of `@property`). `@property_name.setter` works by having a `setter()` method on the descriptor objects `@property` returns. This `setter()` method changes the behavior of the property's `__set__` method (but not the value as an instance attribute, then the function binding descriptor wouldn't work). That is most of the features I regularly use in Python. There are a few simplifications and some things are left out, but for the most part this should be accurate. However, I left out a lot of information about the standard library (built-in functions and modules). Information about them can be found [here][4] and [here][5]. ## Internals This section details how a certain Python interpreter ([CPython][6]) actually runs Python code. Most things discussed here don't apply to other interpreters, although some concepts might be the same. ### Processing order When Python code is run, it is **lexed**, **parsed**, converted to **bytecode**, and then run. #### Lexing Lexing, or tokenizing, is the process of breaking code up into groups of symbols so it is easier to process later. A simple example of lexing (not with Python code) could look like: ``` 1 + 2*(3 +4- 5) -> NUM 1 OP + NUM 2 OP * OPEN_PAREN NUM 3 OP + NUM 4 OP - NUM 5 CLOSE_PAREN ``` Note that this takes care of unused whitespace and other things that might complicate the process later on. TODO: explain python lex #### Parsing Parsing converts tokenized code into a tree structure that is easier for code to process. For example, using the arithmetic expression from earlier: ``` NUM 1 OP + ... NUM 5 CLOSE_PAREN -> ADD( 1, MUL( 2, SUB( ADD(3,4), 5 ) ) ) ``` This takes care of things like parentheses that don't really affect how code actually runs. TODO: explain python parse #### Bytecode: What is it? Bytecode is an **intermediate language**, which means that code is translated to it before being run. For example, again with the arithmetic expression: ``` ADD(1, MUL(...)) -> PUSH 1 PUSH 3 PUSH 2 PUSH 4 ADD // 3 + 4 PUSH 5 SUB // 3 + 4 - 5 MUL // 2 * (3 + 4 - 5) ADD // 1 + 2 * (3 + 4 - 5) ``` That code is actually not the bytecode, just a simplified version. Bytecode would actually be represented as a sequence of bytes, like in the following example (with bytes as hexadecimal): ``` PUSH 1: 00 01 // the stack is currently 1 PUSH 3: 00 03 // the stack is currently 1 3 PUSH 2: 00 02 // the stack is currently 1 3 2 PUSH 4: 00 04 // the stack is currently 1 3 2 4 ADD: 01 // the stack is currently 1 3 6 PUSH 5: 00 05 // the stack is currently 1 3 6 5 SUB: 02 // the stack is currently 1 3 1 MUL: 03 // the stack is currently 1 3 ADD: 01 // the stack is currently 4 ``` Here, the **opcode** for `PUSH` is `00` (with 1 parameter), `ADD` is `01`, etc. The bytecode for Python is complicated and has many operations (detailed [here][7]), so I will not fully document it here. It is stack-based, which means most of its instructions operate on a data stack, similar to the arithmetic example above. This stack contains Python objects. There are also a set of slots, which are used to store variables, constants, names, etc. Here are some common instructions: ``` POP_TOP: remove the top element of the stack (TOS) BINARY_ADD: pop the TOS and the element below the TOS (TOS1), add them, and push it back onto the stack BINARY_SUBTRACT: see above (-) BINARY_MULTIPLY: see above (*) BINARY_FLOOR_DIVIDE: see above (//) BINARY_TRUE_DIVIDE: see above (/) STORE_FAST(slot_num): pop the TOS and store to a slot LOAD_FAST(slot_num): read a slot and push to the stack LOAD_CONST(slot_num): load a constant and push it CALL_FUNCTION(arg_count): pop the some elements and call the last one as a function with the other elements RETURN_VALUE: return from a function JUMP_ABSOLUTE(where): jump to the <where>th bytecode instruction POP_JUMP_IF_TRUE(where): pop the TOS, if true, jump to <where> POP_JUMP_IF_FALSE: ... GET_ITER: pop the TOS, convert to an iterator, and push it FOR_ITER(delta): use the iterator in TOS (w/o popping) and get the next element. If there is no next element, jump ahead by <delta> instructions UNPACK_SEQUENCE(unpack_len): pop an iterable as the TOS and push its elements (there should be <unpack_len>) to the stack ``` #### Bytecode: How is it made? In Python, bytecode for arithmetic expressions is implemented similarly to the example with the arithmetic expressions. For example, ``` a + b: <bytecode for a> <bytecode for b> BINARY_ADD a - b: <bytecode for a> <bytecode for b> BINARY_SUBTRACT a * b: <a> <b> BINARY_MULTIPLY a / b: ... a // b: ... a(b, c, d): <a> <b> <c> <d> CALL_FUNCTION(3) ... ``` Bytecode for an `<expression>` line looks like `<bytecode for <expression>> POP_TOP` Bytecode for a `<variable> = <expression>` line in the global scope looks like `<bytecode for <expression>> STORE_NAME(<variable>)` Bytecode for a `<variable> = <expression>` line in the local scope looks like `<bytecode for <expression>> STORE_FAST(<variable slot>)` Bytecode for a `<var1>, <var2>, <var3> = <expression>` line looks like `UNPACK_SEQUENCE(3) <assign to var1, global or local> <assign to var2> <assign to var3>` Bytecode for a `(<var1>, <var2>), <var3> = <expression>` line looks like `UNPACK_SEQUENCE(2) UNPACK_SEQUENCE(2) <assign to var1> <assign to var2> <assign to var3>` Bytecode for an `if <expr>: <code>` line looks like `<bytecode for <expr>> POP_JUMP_IF_TRUE(END) <bytecode for <code>> LABEL(END)` (this will sometimes be optimized) Bytecode for an if-elif-else statement looks similar, but more complicated Bytecode for a `while <expr>: <code>` line looks like `LABEL(START) <bytecode for if <expr>: <code>> JUMP_ABSOLUTE(START)` Bytecode for a `for <lvalue> in <expr>: <code>` line looks like `<bytecode for <expr>> GET_ITER LABEL(START) FOR_ITER(END)`<br> `<assign to <lvalue>> <bytecode for <code>> JUMP_ABSOLUTE(START) LABEL(end)` TODO: `def`, `class` Python applies some rudimentary optimizations, like converting `JUMP_ABSOLUTE`s to more efficient `JUMP` instructions. [0]: https://repl.it/talk/challenge/Introduction-To-Python/7484 [1]: https://docs.python.org/3/reference/expressions.html#operator-precedence [2]: https://www.python.org/download/releases/2.3/mro/ [3]: https://docs.python.org/3/reference/datamodel.html#special-method-names [4]: https://docs.python.org/3/library/functions.html [5]: https://docs.python.org/3/library/ [6]: https://github.com/python/cpython [7]: https://docs.python.org/3/library/dis.html#python-bytecode-instructions
8
posted by pyelias (614) 5 months ago
โ–ฒ
35
Basic Platformer With Javascript and HTML
# BASIC PLATFORMER TUTORIAL **End Result:** https://basic-platformer--luchutton.repl.co/ **The Code:** https://repl.it/@LucHutton/Basic-Platformer ## Prerequisites And Advice: For this tutorial you should already have a basic understanding of programming (preferrably in Javascript) as **I will not be explaining basic Javascript concepts in this tutorial.** If you wish to learn some/more Javascript I recommend you visit: https://developer.mozilla.org/en-US/docs/Web/JavaScript ## Setup: It is best if you follow along using a **HTML + CSS + JS REPL**, as when created the REPL will already have all the boiler plate stuff, but also because you will be able to see the result straight away on the same screen (when you run the code). The first (and most important) line of code you will write will be in the ```index.html``` file. Don't worry this is the only bit of HTML you will have to write :). Place the line of code after the opening ```body``` tag but before the ```script``` tag, like so: ```html <body> <canvas id="canvas" width="512" height="512"></canvas> <script src="script.js"></script> </body> ``` Make sure the id of the ```canvas``` is "canvas", but you can change the width and height to whatever number you want as long as the **width and height are multiples of 32!** ---- Navigate yourself to the ```script.js``` file where we will be spending the remainder of this tutorial. The first thing that we need to do is to create a place in which we can store the attributes of the player, this will be done using an **Object**. For starters our player needs to have an X coordinate, a Y coordinate, a Width and a Height. For simplicity's sake the player will be a 32 x 32 pixel square, and the initial X will be set to half the ```canvas``` width, and Y will be set to half the ```canvas``` height. ```javascript const player = { x: 256, y: 256, width: 32, height: 32 } ``` **Make sure the final value doesn't have a comma after it!** At the top of the Javascript file we need to create a reference to the ```canvas``` element we created in the first step like so: ```javascript const c = document.getElementById("canvas").getContext("2d"); ``` It basically gets the ```canvas``` element -> specifys that we will be working in two dimensions -> sets the value of c to that. Now we need a way to draw the player at its location with its size. To do this we will create a **draw function**, inside it we will need to specify the fill colour of the player and how to draw the player. To specify the colour to fill the next object drawn, you update the canvas' fill colour. ```javascript c.fillStyle = "red"; ``` You can set the colour to whatever you want. On the next line you need to actually draw the player rectangle using a canvas function called ```fillRect``` which draws and fills the rectangle at the same time. The ```fillRect``` function takes four parameters in the following order: an x value, a y value, a width and a height. ```javascript c.fillRect(player.x, player.y, player.width, player.height); ``` As shown above, you access the attributes of an object using the template: ```objectName.attribute```. To call this draw function: at the bottom of the Javascript file put: ```javascript draw(); ``` If you followed the steps above correctly your Javascript code should look like this: ```javascript const c = document.getElementById("canvas").getContext("2d"); const player = { x: 256, y: 256, width: 32, height: 32 } function draw(){ c.fillStyle = "red"; c.fillRect(player.x, player.y, player.width, player.height); } draw(); ``` When you run this code you should see a red square appear somewhere in the top-left of your screen. Now remove the line calling the draw function as it was only for testing. --- ## Going Loopy Every meaningful game needs to have a main function that is looped, to update and draw new things so fast that it is almost imperceptible to the human eye. For our game we need a main function that is called when all the updating for one frame is done. So create a function called main, and inside you can put a call to the draw function: ```javascript function main(){ draw(); } ``` In this state the main function will only be called once, to fix this we need to put another line below the call to draw that re-runs the main function. ```javascript function main(){ draw(); requestAnimationFrame(main); } ``` It basically allows the HTML to render what is drawn and then calls the main function again. --- ## Startup If you run the code you will notice that nothing happens, this is because nothing calls the main function in the first place. What we need is something that runs when the page is fully loaded, luckily Javascript has a function just for this: ```javascript window.onload = function(){ main(); } ``` When the page loads, whatever is in the function will be executed, so if you run the code now you should now see the red square appear again! --- ## Level Making And Drawing We will be making this game's level using a tile map based system because it is easy to use and design. The main concept is that there will be a multi-line string with each line being a certain length long filled with either a 0 or a 1 (Air or a Wall). You can define the level variable like: ```javascript const level = `0000000000000000 0000000000000000 0010000000000000 0000000000001111 0000111000000000 0000000000011111 0000000000000000 0000000000111111 0000000000011000 1110000000000000 0000000010000110 0001111111100000 0000000000000000 0000000000000000 0000000001111110 0000000000000000`; ``` This gives us the ability to create multiple levels really easily. Feel free to tweak the positions of the 1s and 0s until you see fit. *Side Note: Levels should really be defined in external text files, but file handling would distract us too much from the making of the game so I decided just to define it in the Javascript.* Before being able to use this level data we need to make a function that parses it into a 2D Array (Arrays within arrays (but only to one layer)). We need to: + Split the string up by line + Split each line up by character + Return that result To split by line and store the result in an array we can do: ```javascript const lines = lvl.split("\n"); ``` Where lvl is the level data that we pass to the function. To split each line in the array by characters we need to use the ```map``` function *(Introduced in ES5)*. ```javascript const characters = lines.map(l => l.split("")); ``` If you are unsure on how to use the ```map``` function refer to [this](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map). The final level-parsing function should look like: ```javascript function parse(lvl){ const lines = lvl.split("\n"); const characters = lines.map(l => l.split("")); return characters; } ``` To make the level data accessible to all of the program we need to define a variable in the global scope, at the top of the Javascript file, below the ```c``` variable, write: ```javascript let currentLevel; ``` This just tells Javascript that we want this variable to be declared but that we don't need to use it yet. In the ```window.onload``` function update the value of ```currentLevel``` to the return value of the parse function: ```javascript window.onload = function(){ currentLevel = parse(level); main(); } ``` Now all we need to do is draw the level! In the draw function we need to loop through each of the level data lines and on each line we need to loop through each character, and if the character is a 1 then we need to draw a square at the position on canvas relative to the character's position. Also before the looping we need to define a new colour for walls! ```javascript function draw(){ c.fillStyle = "red"; c.fillRect(player.x, player.y, player.width, player.height); c.fillStyle = "black"; for (let row = 0; row < currentLevel.length; row++) { for (let col = 0; col < currentLevel[0].length; col++) { if (currentLevel[row][col] === "1") { c.fillRect(col * 32, row * 32, 32, 32); } } } } ``` Here we are using that ```fillRect``` function again, but instead of the player we are drawing each wall tile. If you now run the code you should see squares that match what is written in the level variable. --- ## Handling User Input Our game right now is pretty boring as there is nothing to do, only stare at the beautifully arranged squares that should now populate you screen. To fix this we need to add a way to listen for keyboard input and move the player based on which key is pressed. At the top of the Javascript file write the line: ```javascript let keysDown = {}; ``` This is an empty **object** which will hold the keys currently pressed. If you are wondering why we need to store current keys down it is because this way allows multiple keys to be pressed at once. To listen for key presses, and then store that key press in the```keysDown``` variable we will use an ```eventListener```: ```javascript addEventListener("keydown", function(event){ keysDown[event.keyCode] = true; }); ``` This function executes whenever the user presses a key on their keyboard, the key pressed information is stored in the event parameter, it then sets the key to true in ```keysDown```. Now we need a similar function that executes when a key is released: ```javascript addEventListener("keyup", function(event){ delete keysDown[event.keyCode]; }); ``` It removes the entry of the released key from ```keysDown```. --- To detect and act upon keys that are pressed we need to create an input function that checks whether certain keys are pressed. ```javascript function input(){ if(65 in keysDown){ player.x -= 3; } if(68 in keysDown){ player.x += 3; } } ``` In Javascript keys are represented numerically, in this example 65 is the number for the "A" key and 68 is the number for the "D" key, if you wish to find out the keycodes for other keys you can use [this website](https://keycode.info/). In main you need to call this function so that input is checked every time main is executed. If you run the code you should see that when you press "A" or "D" the player moves horizontally, but you may also notice that the red square's "previous states" are still visible, to fix this write the following line at the top of the draw function: ```javascript c.clearRect(0, 0, canvas.width, canvas.height); ``` --- ## Horizontal Collisions When moving the red square you will notice that it passes straight through walls, this is because we haven't defined any collisions yet! But before we do collisions we need to create a function called ```getTile``` that gets the tile at a certain X and Y value: ```javascript function getTile(x,y){ return(currentLevel[Math.floor(y / 32)][Math.floor(x / 32)]); } ``` To start collisions we need to define another player attribute called ```speed```, this will determine how fast the player can move horizontally: ```javascript const player = { x: 256, y: 256, width: 32, height: 32, speed: 3 } ``` Now move back to the input function and add two if statements around the updating of the ```player.x``` value, and update the previous code to add ```player.speed``` instead of 3: ```javascript function input(){ if(65 in keysDown){ if (getTile((player.x - player.speed) + 1, player.y + 16) !== "1") { player.x -= 3; } } if(68 in keysDown){ if (getTile(((player.x + player.width) + player.speed) - 1, player.y + 16) !== "1") { player.x += 3; } } } ``` This checks whether the tile, when the player will move by ```player.speed```, at the player's location is a wall or air, if it is air then the player is allowed to move in the direction, else do nothing. If you now run the code, the red square should stop whenever it hits a wall. --- ## Gravity This section will involve a little bit of physics, if you are unaware of how gravity and jumping works you can look at [this website](https://www.khanacademy.org/science/physics/work-and-energy/work-and-energy-tutorial/a/what-is-gravitational-potential-energy), but note that this knowledge isn't required to follow along the next section (it just clarifies things). Firstly we need to update the player object with three attributes required for calculating Gravitational Potential Energy, they are: + Mass (mass) + Kinetic Energy On The Y-Axis (yke) + Gravitational Potential Energy (gpe) ```javascript const player = { x: 256, y: 256, width: 32, height: 32, speed: 3, mass: 64, yke: 0, gpe: 0 } ``` Now we need to create a function to update the player's Y position, the player's Y Kinetic Energy and the player's GPE, but first we will create a function that takes the player as a parameter and calculates the GPE of it. GPE = mass * 9.8 * height. As we will be working in pixels instead of meters we will need to divide the Gravitational Field Strength (9.8) by a million so it scales correctly, and as (0,0) on ```canvas``` is the top left we need to take the player's Y value from the height of the canvas (512 in my case), and finally so the GPE doesn't increase so quickly per pixel we will divide the player's 'height' by 32. ```javascript function calcGPE(obj) { return obj.mass * (9.8 / 1000000) * ((canvas.height - obj.height) - (obj.y / 32)); } ``` Now that that is out of the way we can create a function called ```gravity``` that takes the player as a parameter and: 1. Takes yke away from y 2. Takes GPE away from yke 3. Recalculates GPE ```javascript function gravity(obj){ obj.y -= obj.yke; obj.yke -= obj.gpe; obj.gpe = calcGPE(obj); } ``` Now add a call to ```gravity``` in the ```main``` function and pass ```player``` to it. Now if you run the code you should see that the red square now falls off the screen in a realistic manner. --- ## Vertical Collisions When our red square falls, it goes straight through the walls/floors, to fix this we need to add some more code to the gravity function. Firstly we will check for downwards collisions (landing on the floor), to do this we need to utilise the check squares function to see if the tile at the player's feet is a wall, if yes then we need to set the player's Y Kinetic Energy to 0 and move the player up until it looks seamless with the floor. Also as a preventative measure against future bugs we will need to check that the player is actually falling (Y Kinetic Energy is less than 0). ```javascript // Place Below Previous Code Written if (getTile(obj.x + 32, (obj.y + 32)) !== "0" || getTile(obj.x, (obj.y + 32)) !== "0") { if (obj.yke <= 0){ obj.yke = 0; obj.y -= (obj.y % 32); } } ``` Now we need to do the same for upwards collisions, and also we should only check one vertical collision if the other is false. ```javascript if (getTile(obj.x, obj.y) !== "0" || getTile(obj.x + 32, obj.y) !== "0") { if (obj.yke >= 0){ obj.yke = -0.5; obj.y += 1; } } ``` This does almost the same thing except it sets the Y Kinetic Energy to a small negative number so that it doesn't get stuck on the wall above. Your ```gravity``` function should now look like this: ```javascript function gravity(obj) { obj.y -= obj.yke; obj.yke -= obj.gpe; obj.gpe = calcGPE(obj); if (getTile(obj.x, obj.y) !== "0" || getTile(obj.x + 32, obj.y) !== "0") { if (obj.yke >= 0){ obj.yke = -0.5; obj.y += 1; } } else { if (getTile(obj.x + 32, (obj.y + 32)) !== "0" || getTile(obj.x, (obj.y + 32)) !== "0") { if (obj.yke <= 0){ obj.yke = 0; obj.y -= (obj.y % 32); } } } } ``` On running the code you can see that the red square stops when it hits a wall! --- ## Jumping Congratulations if you made it this far (this tutorial is very long...), this will be the final section before the basic platformer is complete! The final thing that we need to add is jumping. If we just move the player's Y value by a certain amount the jumping will look very unnatural, so instead we will increase the player's Y Kinetic Energy which gives it a nice realistic looking jump. The keycode for the "W" key (the jump key) is 87, so in the ```input``` function add a check for if 87 is in ```keysDown``` and if so increase the player's Y Kinetic Energy by however much you desire (5-8 is generally good), but to disallow the player to jump when directly below a wall we need to add a check to see if they are (and disallow jumping). ```javascript if (87 in keysDown && player.yke === 0) { if (getTile(player.x,player.y - 1) !== "1" && getTile(player.x + 32,player.y - 1) !== "1"){ player.yke += 8; } } ``` This also checks whether the player's Y Kinetic Energy is 0 (is on floor) to prevent mid-air jumping. And now... if you run the code you should be able to jump! --- ## Finished Thank you for taking the time to read through this tutorial! If you enjoyed this tutorial please give me an **upvote**! by lucdadukey
7
posted by LucHutton (124) 5 months ago
Load more