<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[WebWideWit]]></title><description><![CDATA[WebWideWit]]></description><link>https://webwidewit.com</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1717510411449/URCfpqQb2.png</url><title>WebWideWit</title><link>https://webwidewit.com</link></image><generator>RSS for Node</generator><lastBuildDate>Sun, 19 Apr 2026 01:29:03 GMT</lastBuildDate><atom:link href="https://webwidewit.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Galaxy classification using the random forest algorithm]]></title><description><![CDATA[If you’ve read any current research on the morphological classification of galaxies, you probably know that CNNs (convoluted neural networks) are one of the most popular choices of classifiers used. In comparison, many older articles use algorithms l...]]></description><link>https://webwidewit.com/galaxy-classification-using-the-random-forest-algorithm</link><guid isPermaLink="true">https://webwidewit.com/galaxy-classification-using-the-random-forest-algorithm</guid><category><![CDATA[galaxy]]></category><category><![CDATA[coding]]></category><category><![CDATA[astrophysics]]></category><category><![CDATA[Computer Science]]></category><category><![CDATA[Random Forest]]></category><category><![CDATA[CNNs (Convolutional Neural Networks)]]></category><dc:creator><![CDATA[Fatima Ali]]></dc:creator><pubDate>Thu, 25 Dec 2025 06:11:10 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1763089046757/56fec988-284c-4f40-b78c-ace8249bb616.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>If you’ve read any current research on the morphological classification of galaxies, you probably know that CNNs (convoluted neural networks) are one of the most popular choices of classifiers used. In comparison, many older articles use algorithms like Random Forest and XGBoost (<strong>eXtreme Gradient Boosting</strong>) which are based on more traditional classifiers like decision trees. In this article, I’ll revisit the random forest approach, and compare it to a neural network in the next part to find out which choice is better!</p>
<h3 id="heading-the-morphological-classification-of-galaxies">The morphological classification of galaxies</h3>
<p>Okay, short astronomy lesson first. In the 1920’s, Edwin Hubble created a system commonly called the ‘Hubble tuning fork diagram’, to classify galaxies based on their appearances while studying their evolution. This system is still very commonly used today (<em>although now considered a bit of an oversimplification</em>), and is divided into two distinct parts: elliptical, and spiral galaxies. The ellipticals are further divided into 7 subcategories, with E0 being the least elliptical (a.k.a the roundest), and E7 being the most elliptical. Spirals on the other hand, are broken down further into ‘S’ (normal spirals) and ‘SB’ (barred spirals), based on how the spiral arms project from the center of the galaxy. Both of these divisions are then given suffixes from a-c, with ‘a’ being the tightest wound spiral and ‘c’ being the loosest. There is also a special type of galaxies placed right where the prongs of the ‘tuning fork’ meet its stem, S0 or lenticular galaxies, which are considered transitional galaxies exhibiting features of both spirals and ellipticals. One thing that this diagram doesn’t account for however, is the existence of irregular galaxies, which are neither elliptical nor spiral in shape, usually formed as a result of collisions/interactions amongst galaxies.</p>
<p><img src="https://assets.science.nasa.gov/dynamicimage/assets/science/missions/hubble/releases/1999/10/STScI-01EVVPETV40FFV593Y8GKN83WH.jpg?w=850&amp;h=700&amp;fit=clip&amp;crop=faces%2Cfocalpoint" alt="The Hubble Tuning Fork – Classification of Galaxies" /></p>
<p>But why is any of this important? Well, even though Hubble’s original concept of galaxy evolution wasn’t <em>completely</em> accurate, the appearance of a galaxy can actually tell us a lot about what goes on inside. The arms of spiral galaxies are sites of star formation, containing large amounts of hot gas and dust that give birth to bright, hot, and young stars (most type O and B on the HR diagram). Elliptical galaxies on the other hand are referred to as ‘red and dead’ galaxies, since they contain less gas and dust, and are populated by older, redder stars with long lifespans.</p>
<h3 id="heading-cnns-and-random-forests">CNNs and Random Forests</h3>
<p><em>For the purpose of this model, I’m using labelled images from the publicly available Galaxy Zoo dataset (</em><a target="_blank" href="https://www.zooniverse.org/projects/zookeeper/galaxy-zoo/"><em>https://www.zooniverse.org/projects/zookeeper/galaxy-zoo/</em></a><em>) on Zooniverse.</em></p>
<p>As I mentioned earlier, two of the most common approaches to classification are CNNs and Random Forest. The <em>simplest</em> way I can explain how CNNs work is by using convolution layers that detect features like edges and lines to create feature maps for different filters (more about this in part 2). While this method is extremely accurate, the problem with neural networks is that they sort of act like a black box. What I mean by this is that the output of such classifiers is not interpretable, so you won’t get much insight as to why a galaxy is classified the way it is. Keep in mind that this holds true mostly for <strong>vanilla</strong> CNNs, and that new techniques detecting which regions most influence the classifier’s decision have been developed.</p>
<p>This brings me to Random Forests, which is the method I’m going to use to classify the galaxy zoo dataset in this article. Like a regular forest is made up of regular trees, in machine learning, a random forest is made up of multiple <strong>decision trees</strong>. A decision tree is basically a flowchart-style model that starts at the ‘root node’, which is the main question derived from the features of the dataset. From then onwards, it asks a series of yes/no questions, splitting the data into subsets based on specific features, reaching a conclusion (leaf node) when there are no more useful questions to be asked.</p>
<p><img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSBtq1Ujn0HJ0ZGSinHUNfCBbNSuM-_UnEbAw&amp;s" alt="Block Decision Tree Diagram | Download ..." class="image--center mx-auto" /></p>
<p>Decision trees have their own share of problems, which include overfitting, where the trees get so deep that they memorize the training data instead of underlying patterns, and instability. In an attempt to reduce this, we use random forests instead of depending on the output of one, singular tree. There are roughly three main parts to the working of a random forest: bootstrapping, random feature selection (feature bagging), and aggregation. You may have heard this type of random forest, where the decision trees work parallel to each other producing independent results, be sometimes referred to as Bagging (bootstrapping + aggregation).</p>
<p><strong>1. Bootstrapping</strong> - The selection of <strong>random</strong> data values within the original dataset for each decision tree.<br /><strong>2. Random feature selection</strong> - The selection of a random subset of the total features to train each individual decision tree on. This helps decorrelate them, reducing the chances of getting overfitted results.<br /><strong>3. Aggregation</strong> - Averaging (in case of regression), or taking a majority vote (in case of classification) from each decision tree to reach a conclusion.</p>
<p><img src="https://uploads.sitepoint.com/wp-content/uploads/2025/01/1737778055The-Random-Forest-Algorithm-in-Machine-Learning.png" alt="Random Forest Algorithm in Machine Learning With Example - SitePoint" /></p>
<h3 id="heading-choosing-defining-features-for-galaxies">Choosing defining features for galaxies</h3>
<p>So now we know that we need to train the random forest on some features of the galaxies, but what features exactly do we use, given our dataset consists of images?<br />Well, for each image in the training and testing data, I computed a small set of non‑parametric structural features that define how the galaxy’s <strong>light</strong> is distributed. To do this, I treated each image as a 2D light distribution, and found the flux-weighted centroid (the center that lies near the brightness peak), which I then used to find the radial distance of each pixel. This is because galaxies are roughly centrally concentrated, so defining everything with respect to the center lets us build accurate radial profiles and flux fractions.  </p>
<p>Radial profiles are the radii containing some portion of the total light of the galaxy. I use three common radial profiles as features to train my random forest: R20, R50, and R80, the radii at which the cumulative flux reaches 20%, 50%, and 80% of itself respectively. Since elliptical galaxies have a lot of their light packed into a small radius, we expect them to have a smaller R20 and R50, whereas other, more spread out galaxies tend to have higher values for these features.  </p>
<p>Using these radial profiles, we can calculate concentration indices, which quantify how centrally concentrated the light is. As my fourth and fifth features, I use the 50/20 and 80/20 concentration indices, which can be calculated as R50/R20 and R80/R20 respectively. As we outlined earlier, elliptical galaxies tend to be more centrally concentrated, and thus have <strong>higher concentration indices</strong> as compared to other featured galaxies such as spirals and irregulars.</p>
<p>For my next feature, I calculated the Gini coefficient, a term you may have heard of in Economics used to calculate income inequality. In our case, the Gini Coefficient or G is value from 0-1 used to measure how unequally the total light is distributed among pixels. If a few pixels are very bright and most are faint, the distribution is highly unequal (high G). If brightness is uniform, Gini is low.</p>
<p><img src="https://kimfetti.github.io/images/gini_explanation.png" alt="Measuring Statistical Dispersion with the Gini Coefficient" class="image--center mx-auto" /></p>
<p>The final feature I calculated was M20, the second moment of the brightest 20% of pixels. The value of M20 describes how spatially extended the brightest regions are. If the brightest 20% of light is near the center like in ellipticals, you get a low M20; if it is more off‑center, M20 becomes larger.</p>
<h3 id="heading-extracting-features-computationally">Extracting features computationally</h3>
<p>To start with the model, we’ll first need to import and filter our dataset. As mentioned before, I’m using data from the galaxy zoo project, which contains 120,000 classified images from the Hubble Space Telescope. More specifically, I’ll use the 'smooth-or-featured-gz2_smooth' label column within the dataset to filter galaxies that exhibit smooth, elliptical shapes vs. featured galaxies that have arms, bars or irregularities based citizens' votes in the Galaxy Zoo project. A vote&gt;0.5 means the galaxy is smooth, and &lt;0.5 means its featured. You can import the <strong>training</strong> dataset into your code as follows:  </p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> galaxy_datasets <span class="hljs-keyword">import</span> gz2
<span class="hljs-keyword">import</span> numpy <span class="hljs-keyword">as</span> np
<span class="hljs-keyword">import</span> pandas <span class="hljs-keyword">as</span> pd
<span class="hljs-keyword">from</span> PIL <span class="hljs-keyword">import</span> Image

catalog, label_cols = gz2(
    root=<span class="hljs-string">"data"</span>,
    train=<span class="hljs-literal">True</span>,
    download=<span class="hljs-literal">True</span>
)
</code></pre>
<p>But, before we can actually use these images, we need to clean them up a bit. I wrote a tiny function that does this by converting the images to grayscale and roughly subtracting their background (~5th percentile).</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> os
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">load_image_gray</span>(<span class="hljs-params">path</span>):</span>
    im = Image.open(path).convert(<span class="hljs-string">"L"</span>)        <span class="hljs-comment">#grayscale</span>
    arr = np.array(im, dtype=np.float32)
    arr -= np.percentile(arr, <span class="hljs-number">5</span>)             <span class="hljs-comment">#rough background subtraction</span>
    arr[arr &lt; <span class="hljs-number">0</span>] = <span class="hljs-number">0</span>
    <span class="hljs-keyword">return</span> arr
</code></pre>
<p>Now, we can start on a function to calculate the 7 features outlined in the part above from each image.</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">compute_features</span>(<span class="hljs-params">img</span>):</span>
    yy, xx = np.indices(img.shape)
    <span class="hljs-comment">#computing a flux-weighted centroid</span>
    total_flux = img.sum()
    <span class="hljs-comment">#sub pixel center that lands near the brightness peak</span>
    x_centroid = (xx * img).sum() / total_flux 
    y_centroid = (yy * img).sum() / total_flux
    r = np.sqrt((xx - x_centroid)**<span class="hljs-number">2</span> + (yy - y_centroid)**<span class="hljs-number">2</span>)
    r_max = r.max()
    bins = np.linspace(<span class="hljs-number">0</span>, r_max, num=<span class="hljs-number">20</span>) <span class="hljs-comment">#20 bins for radius</span>
    r_flat = r.ravel()
    img_flat = img.ravel()
    which_bin = np.digitize(r_flat, bins) - <span class="hljs-number">1</span>
    mean_flux = np.zeros(len(bins))
    r_mid = []
    <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(len(bins)):
        mean_flux[i] = img_flat[which_bin == i].mean()
        r_mid.append(<span class="hljs-number">0.5</span> * (bins[i] + bins[i<span class="hljs-number">-1</span>]) <span class="hljs-keyword">if</span> i &gt; <span class="hljs-number">0</span> <span class="hljs-keyword">else</span> bins[<span class="hljs-number">0</span>] / <span class="hljs-number">2</span>)

    cumulative_flux = np.cumsum(mean_flux)
    total_cumulative_flux = cumulative_flux[<span class="hljs-number">-1</span>]
</code></pre>
<p>This piece of code basically adds up the brightness for each pixel in the image and stores it in <code>total_flux</code>. Then, treating the image as a 2d distribution, it calculates the centroid, which is where the brightness peaks, and uses it to calculate the radial distances for each pixel. It then creates 20 concentric annuli (area betw. 2 concentric circles) from the center to the max radius, taking the mean flux of all pixels in that annulus to calculate approximate azimuthally averaged radial profiles as shown below:</p>
<pre><code class="lang-python">    R20 = r_mid[np.searchsorted(cumulative_flux, <span class="hljs-number">0.2</span> * total_cumulative_flux)]
    R50 = r_mid[np.searchsorted(cumulative_flux, <span class="hljs-number">0.5</span> * total_cumulative_flux)]
    R80 = r_mid[np.searchsorted(cumulative_flux, <span class="hljs-number">0.8</span> * total_cumulative_flux)]
    C_80_20 = R80 / R20 <span class="hljs-keyword">if</span> R20 !=<span class="hljs-number">0</span> <span class="hljs-keyword">else</span> <span class="hljs-number">0</span> 
    C_50_20 = R50 / R20 <span class="hljs-keyword">if</span> R20 !=<span class="hljs-number">0</span> <span class="hljs-keyword">else</span> <span class="hljs-number">0</span>
    f = np.sort(img.ravel().copy())
    f[f &lt; <span class="hljs-number">0</span>] = <span class="hljs-number">0</span>
</code></pre>
<p>The radial profile calculations use Numpy’s <code>searchsorted</code> function to find the bins where the cumulative flux first reaches 20%, 50% and 80% of itself respectively, taking their midpoints as R20, R50 and R80.<br />Following this, it finds the concentration indices by calculating ratios of the radial profiles.</p>
<p>We can now move on to calculating the Gini coefficient and M20.</p>
<pre><code class="lang-python">
    <span class="hljs-comment"># Gini coefficient</span>
    <span class="hljs-comment">#Gini coeff = inequality in brightness across the pixels </span>
    n = f.size
    mean_f = f.mean()
    i = np.arange(<span class="hljs-number">1</span>, n+<span class="hljs-number">1</span>)  <span class="hljs-comment"># 1..n</span>
    G = ( (<span class="hljs-number">2</span>*i - n - <span class="hljs-number">1</span>) * f ).sum() / (mean_f * n * (n<span class="hljs-number">-1</span>) + <span class="hljs-number">1e-8</span>)

    xx_f = xx.ravel()
    yy_f = yy.ravel()
    r2 = (xx_f - x_centroid)**<span class="hljs-number">2</span> + (yy_f - y_centroid)**<span class="hljs-number">2</span>
    M_i = f * r2
    idx_desc = np.argsort(f)[::<span class="hljs-number">-1</span>]
    f_desc = f[idx_desc]
    M_desc = M_i[idx_desc]
    cum_flux_desc = np.cumsum(f_desc)
    total_flux = f_desc.sum()
    k = np.searchsorted(cum_flux_desc, <span class="hljs-number">0.2</span> * total_flux) + <span class="hljs-number">1</span>
    M20_sum = M_desc[:k].sum()
    positive = f &gt; <span class="hljs-number">0</span>
    f = f[positive]; xx_f = xx_f[positive]; yy_f = yy_f[positive]
    r2 = (xx_f - x_centroid)**<span class="hljs-number">2</span> + (yy_f - y_centroid)**<span class="hljs-number">2</span>

    M_i = f * r2
    M_tot = M_i.sum()

    idx_desc = np.argsort(f)[::<span class="hljs-number">-1</span>]
    f_desc = f[idx_desc]
    M_desc = M_i[idx_desc]

    cum_flux = np.cumsum(f_desc)
    k = np.searchsorted(cum_flux, <span class="hljs-number">0.2</span> * cum_flux[<span class="hljs-number">-1</span>]) + <span class="hljs-number">1</span>
    M20_sum = M_desc[:k].sum()

    M20 = np.log10(M20_sum / (M_tot + <span class="hljs-number">1e-8</span>))
    features = np.array([
    R20, R50, R80,
    C_80_20, C_50_20,
    G, M20
    ], dtype=np.float32)

    <span class="hljs-keyword">return</span> np.array(features, dtype=np.float32)
</code></pre>
<p>First, I flatten all the pixel values into a one‑dimensional list and plug them into the standard Gini formula (attached below), which gives me the Gini coefficient G. Pixels that are all about the same brightness give a low Gini while a few very bright pixels and lots of faint ones give a high Gini.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766642631805/0e73e643-05ff-4f4b-a2e0-a3c702b6fad8.png" alt class="image--center mx-auto" /></p>
<p>For M20, I combine each pixel’s brightness with its distance from the galaxy’s center (radial distance). I flatten the x–y pixel coordinates, compute the distance of every pixel from the centroid, and then multiply that distance squared by the pixel’s brightness. That gives a “moment” for each pixel: bright pixels far from the center contribute a lot, faint pixels near the center contribute little. I then sort pixels from brightest to faintest and start adding up their moments until I’ve included just the brightest 20% of the total light. The sum of those moments, divided by the total moment of the whole galaxy and put through a logarithm, gives us M20. Finally, I return everything-radii, concentration, Gini, and M20-as a small feature vector which my random forest model can then use to learn the difference between smooth and featured galaxies.</p>
<p>You can then loop over the Galaxy Zoo training dataset and run this function for each image, storing the results in an array called <code>X_train</code>, and the corresponding labels (1 if label&gt;0.5, 0 otherwise) in <code>y_train</code>. However, be aware that it takes over 1000 minutes to complete… I suggest saving the features of every 100-500 images you process into a CSV file as you go. Also, wrap the entire code in a try-except block so that a small error doesn't ruin hours of computation (<em>I learned this the hard way</em>).</p>
<p>Once you do everything above successfully, you’ll be ready to move onto downloading test data! You can do this the same way we did at the top of this section, just by changing the parameter <code>train</code> to false :</p>
<pre><code class="lang-python">catalog_test, label_cols = gz2(
    root=<span class="hljs-string">"data"</span>,
    train=<span class="hljs-literal">False</span>,
    download=<span class="hljs-literal">False</span> 
)
</code></pre>
<p>You can then run the function <code>compute_features</code> for every image in this dataset too, storing the results in <code>X_test</code> and <code>y_test</code> as done earlier.</p>
<h3 id="heading-building-the-random-forest">Building the random forest</h3>
<p>Finally, we can start with the most awaited section of this article, training and evaluating our random forest model!</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> sklearn.ensemble <span class="hljs-keyword">import</span> RandomForestClassifier
<span class="hljs-keyword">import</span> matplotlib.pyplot <span class="hljs-keyword">as</span> plt
<span class="hljs-keyword">from</span> sklearn.metrics <span class="hljs-keyword">import</span> confusion_matrix
<span class="hljs-keyword">import</span> numpy <span class="hljs-keyword">as</span> np
<span class="hljs-keyword">import</span> pandas <span class="hljs-keyword">as</span> pd

model = RandomForestClassifier(n_estimators=<span class="hljs-number">100</span>)
cols = [<span class="hljs-string">'R20'</span>,<span class="hljs-string">'R50'</span>,<span class="hljs-string">'R80'</span>,<span class="hljs-string">'C_80_20'</span>,<span class="hljs-string">'C_50_20'</span>,<span class="hljs-string">'Gini'</span>,<span class="hljs-string">'M20'</span>]
X_train =  pd.read_csv(<span class="hljs-string">"brightness_features_train.csv"</span>, usecols=cols).values
y_train = pd.read_csv(<span class="hljs-string">'brightness_features_train.csv'</span>, usecols=[<span class="hljs-string">'label'</span>]).values
model.fit(X_train, y_train)
X_test = pd.read_csv(<span class="hljs-string">"brightness_features_test2.csv"</span>, usecols=cols).values
y_test = pd.read_csv(<span class="hljs-string">'brightness_features_test2.csv'</span>, usecols=[<span class="hljs-string">'label'</span>]).values
model.score(X_test, y_test)
y_predict = model.predict(X_test)
cm = confusion_matrix(y_test, y_predict)
plt.imshow(cm, cmap=<span class="hljs-string">'Blues'</span>)
plt.colorbar()
plt.xlabel(<span class="hljs-string">'Predicted labels'</span>)
plt.ylabel(<span class="hljs-string">'True labels'</span>)
plt.xticks([<span class="hljs-number">0</span>, <span class="hljs-number">1</span>], [<span class="hljs-string">'Featured'</span>, <span class="hljs-string">'Smooth'</span>])
plt.yticks([<span class="hljs-number">0</span>, <span class="hljs-number">1</span>], [<span class="hljs-string">'Featured'</span>, <span class="hljs-string">'Smooth'</span>])
plt.title(<span class="hljs-string">'Confusion Matrix'</span>)
plt.show()
</code></pre>
<p>Here, I use Scikitlearn’s <code>RandomForestClassifier</code> function to create a model with 100 decision trees (<code>n_estimators</code>), and feed in the features I computed for the training dataset, plus a binary label (<code>label = 0</code> for featured, <code>1</code> for smooth). Then I point the model at the test set, ask it to predict smooth vs featured, and build a <strong>confusion matrix</strong>: a 2×2 grid that shows, for each true class, how many times the model predicted featured or smooth.</p>
<p>This approach seems reasonable, but in practice, it fails for two reasons. First, the labels are <strong>hugely skewed</strong>: only a few percent of galaxies in Galaxy Zoo are tagged as “featured” by the vote fraction cut I used, while almost everything else is “smooth.” You can actually find the exact number of featured to smooth galaxies by running the following line:</p>
<pre><code class="lang-python">print(<span class="hljs-string">"Train:"</span>, np.bincount(y_train.ravel().astype(int)))
</code></pre>
<p>This returns ‘Train: [ 3116 126644]’, which means that in the training dataset, there are only 3,116 featured galaxies for 126,644 smooth ones, or <strong>2% featured for 98% smooth</strong>.  </p>
<p>That means a dumb model that simply classifies every single galaxy as “smooth”, already hits around 98% accuracy. Second, the seven global brightness features I chose don’t cleanly separate these two classes at survey quality; many featured galaxies have very similar light profiles and Gini/M20 values to smooth ones. Put those together, and the random forest learns that the safest move is to call almost everything smooth. The confusion matrix ends up with one giant block in the “true smooth / predicted smooth” cell, almost nothing in the “true featured / predicted featured” cell, and accuracy that looks great but is scientifically useless.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766641598690/ce61d95b-a2c2-4b36-8872-f2c8f898f509.png" alt class="image--center mx-auto" /></p>
<p>One step I took to fix this, was to weigh the featured galaxies as much as the smooth ones, by creating a new training dataset consisting of a 50-50 ratio of featured to smooth galaxies. This resulted in a confusion matrix looking something like this:  </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766641675373/c7f254c7-64a2-49e8-a69b-ce3bf34cf442.png" alt class="image--center mx-auto" /></p>
<p>This means that the model just predicted more false ‘featured’s, reducing the accuracy of the model to around 55%, which clearly does not fix the problem at all.</p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>So, as we just saw, the random forest approach almost completely failed. It couldn’t reliably pick out featured galaxies from the very skewed Galaxy Zoo data, even when the training set was rebalanced. It either defaulted to calling almost everything smooth or produced lots of false featured flags, which tells us these seven features aren’t enough information for this type of classification. In the next part, we’ll look at how much better CNNs do when they can look directly at the full galaxy images, and then talk about <em>why</em> their decisions differ from the simpler model.</p>
<p>Thank you, and happy holidays!</p>
]]></content:encoded></item><item><title><![CDATA[Simulating the cosmological N-body problem (new)]]></title><description><![CDATA[I was never really one to listen to podcasts (reading is simply superior), but I recently got into the habit of listening to Neil DeGrasse Tyson’s ‘StarTalk’ on my 30-minute bus ride to school, and it]]></description><link>https://webwidewit.com/simulating-the-cosmological-n-body-problem</link><guid isPermaLink="true">https://webwidewit.com/simulating-the-cosmological-n-body-problem</guid><category><![CDATA[astrophysics]]></category><category><![CDATA[Astronomy]]></category><category><![CDATA[Physics]]></category><category><![CDATA[coding]]></category><category><![CDATA[Python]]></category><dc:creator><![CDATA[Fatima Ali]]></dc:creator><pubDate>Fri, 05 Sep 2025 05:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1762141334068/99018a91-cd2d-425a-9af2-1d8c20a06d0b.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I was never really one to listen to podcasts (<em>reading is simply superior</em>), but I recently got into the habit of listening to Neil DeGrasse Tyson’s ‘StarTalk’ on my 30-minute bus ride to school, and it’s become my favorite part of the day. One particular episode that caught my attention was his discussion on the unsolvable 3-body problem. He describes it as "three objects of roughly the same mass trying to maintain a stable orbit" and notes that it is <em>mathematically chaotic</em> to solve. Building on this topic, in this article, I'll explore the n-body problem (a more generalized version of the 3-body problem) and attempt to simulate it computationally using Python!</p>
<h3>What is the N-body Problem?</h3>
<p>While we have equations that can completely predict the interactions of 2 gravitational masses (<em>thank you, Newton</em>), the complexity of systems with 3 or more bodies makes their movement impossible to predict. This is what astrophysicists label as the <strong>n-body</strong> problem. The issue with writing down a general formula to describe the motion of three or more gravitating objects is that we have more unknowns than equations describing them, a.k.a, there are way too many variables to deal with! But what does it mean for these systems to be chaotic? Mathematically, the word ‘chaotic’ is used to refer to a system that is deterministic (non-random), but highly sensitive to its initial conditions (commonly referred to as <em>the butterfly effect</em>). We see this in the n-body problem, where every configuration of these bodies has a large range of potential outcomes that are influenced by the tiniest change in velocity or position.</p>
<h3>Forces and motion</h3>
<p>Now that we know what the problem is, we can try to write it mathematically.</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1762275359034/bed9016a-dd86-4b40-8aeb-9e521c517728.png" alt="" style="display:block;margin:0 auto" />

<p>Here, we have three masses M1, M2 and Mi along with their distance vectors. We can use the equation for the force of attraction between 2 bodies i and j using the Newtonian equation of gravity :</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1762233189775/3e5d3ee2-cd31-48d9-a957-f22993ddcfd6.png" alt="" style="display:block;margin:0 auto" />

<p>Here, rj - ri gives us the position vector from mass i to mass j, along which the force of attraction acts.<br />The reason the force is negative, is because of the inverse relationship of the position and force of attraction vectors. While the position vector Rj-Ri points AWAY from mass i, the force of attraction points TOWARDS IT.</p>
<p>Note : j ≠ i here, because it would make the denominator 0 (<em>also, how would you calculate the attraction from a body to… itself?</em>)</p>
<p>In an n-body simulation, you don’t have just 2 bodies, you have an n number of bodies. This means that you have to <strong>sum up all the forces acting on the body</strong> , as such :</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1762233358641/095c220b-b742-466b-a21c-e2cefceb679e.png" alt="" style="display:block;margin:0 auto" />

<p>The problem with this equation is that it does not account for cases when 2 more more bodies are extremely close, which makes the denominator ~ 0 resulting in singularity. As a solution, I softened the force using the Plummer potential, by adding a tiny constant ε (epsilon) as such:</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768590048855/b272bea8-a96c-47b5-84b9-063e7eafb01c.png" alt="" style="display:block;margin:0 auto" />

<p>This value of ε can be calculated by the following formula, where R is the virial radius, and n is the number of bodies in the simulation:</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768590093708/28c06751-4841-444c-82fb-fd3062571e4b.png" alt="" style="display:block;margin:0 auto" />

<p>Now, to implement this in our code we can write a function as shown below to calculate the forces on each individual star:</p>
<pre><code class="language-python">def calc_forces():
    for star in stars:
        star.F = vector(0, 0, 0)

    for i in range(len(stars)):
        for j in range(len(stars)):
            if i != j:
                rji = stars[j].pos - stars[i].pos
                dist = mag(rji)
                stars[i].F += G * stars[i].m * stars[j].m * norm(rji) math.pow((dist**2 + e**2),3/2)
                stars[j].F -= G * stars[i].m * stars[j].m * norm(rji) / math.pow((dist**2 + e**2),3/2)
</code></pre>
<p>The first loop goes over every star in both lists, and calculates the force of attraction they exert on each other using the formula shown earlier. Notice that we use VPython’s norm() function to normalize the position vector, which removes the extra magnitude from the denominator of the equation. For each individual star (star[i]), I sum up the forces on it from the other stars (star[j]). I then add this value to star[i] and subtract it from star[j] (according to Newton's third law, each action has an equal and opposite reaction)</p>
<h3>Assigning Initial Positions and Velocities Following a Plummer Sphere</h3>
<p>To make the simulation more realistic, I generated the initial positions and velocities of the N stars such that they follow the Plummer sphere model’s mass and potential profiles. The total mass contained within a sphere of radius r is given by the following formula:</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768590284717/886736e2-eccc-4fcf-aabf-c94159e5c13b.png" alt="" style="display:block;margin:0 auto" />

<p>Every star can contain a fraction M(r)/M of the total mass M within its radial position. Considering the mass of the entire system/cluster as 1, we can then generated N random numbers from 0-1 to represent the M(r). The mass fraction could be anything between 0 and 1. It will be 0 if the particle is placed exactly in the center, and it will approach 1 as the particle is placed further away, reaching 1 at infinity. To solve for r, we can rewrite the equation as such, where Xi is the <em>i</em>-th random mass :</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768590463693/16de775c-fff6-442c-a334-d4a77551242c.png" alt="" style="display:block;margin:0 auto" />

<p>Now that we have our radii, we can use spherical polar coordinates (r,θ,φ) to get the actual positions of the stars as a 3d vector.</p>
<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/4/4f/3D_Spherical.svg/250px-3D_Spherical.svg.png" alt="" style="display:block;margin:0 auto" />

<ul>
<li><p>x = r sin(θ) cos(φ)</p>
</li>
<li><p>y = r sin(θ) sin(φ)</p>
</li>
<li><p>z = r cos(θ)</p>
</li>
</ul>
<p>φ (phi), is the azimuthal angle that measures the horizontal rotation and lies between 0 and 2𝜋.<br />θ (theta), is the polar angle that measures vertical tilt (from the z axis in this case) and is calculated such that cos θ lies between -1 and 1.<br />r is the radius we calculated above.</p>
<p>Again, we can calculate uniformly random numbers within these ranges for θ and φ.</p>
<p>Let’s now begin to simulate the n-body problem for an n number of stars using VPython. Like most libraries, you can install VPython using pip and import it using <code>from vpython import *</code></p>
<pre><code class="language-python">G = 1
M = 1
R = 1
N = input("Number of bodies to simulate: ")
stars = []
</code></pre>
<p>To start, I’m defining all the constants as 1 to avoid dealing with really small numbers. Also, I get the number of stars to simulate movement for from the user and store it in the constant N. <code>stars[]</code>, is an empty list we will use to store the stars we create later on in our program.<br />We can then start building our ‘Star’ class, which will act as a template for every star we create.</p>
<pre><code class="language-python">class Star:
    def __init__(self, pos, v, M):
        self.pos = pos
        self.m = M
        self.v = v
        self.p = self.m * self.v
        self.F = vector(0, 0, 0)
        self.a = self.F/self.m
        self.sphere = sphere(pos=self.pos, radius=R / 30, make_trail=True, retain=100, color = vector(random(), random(), random()))
</code></pre>
<p>Every star created using the Star class will have a position (passed in as a parameter), a radius, a velocity, a mass, momentum, acceleration and force (initialized to a vector with magnitude 0). We then draw the star onto the screen by creating a sphere with the given position, radius, and a random color.</p>
<p>We can now use the method discussed above to generate random positions for each of the stars as such:</p>
<pre><code class="language-python">def plummer_sphere(a=1.0):
    Xi = np.random.uniform(0, 1)
    
    r = a / np.sqrt(Xi**(-2/3) - 1)
    
    theta = np.arccos(np.random.uniform(-1, 1)) #polar angle
    phi = np.random.uniform(0, 2 * np.pi) #azimuthal angle
    
    #our actual coordinates 
    x = r * np.sin(theta) * np.cos(phi)
    y = r * np.sin(theta) * np.sin(phi)
    z = r * np.cos(theta)
</code></pre>
<p>But, what about the velocities? Well, the maximum velocity at any given coordinate on the plummer sphere is the escape velocity, given by the formula -</p>
<img src="https://cloudmate-test.s3.us-east-1.amazonaws.com/uploads/covers/61dda0036226ab4f74bef9f2/2509d6ee-80b5-4f40-8945-7135e109ada7.png" alt="" style="display:block;margin:0 auto" />

<p>Therefore the initial velocity of every star in the simulation must be between 0 and the escape velocity (Vesc). In order to get the velocity, we need to know its distribution function, which in this case is given by:</p>
<img src="https://cloudmate-test.s3.us-east-1.amazonaws.com/uploads/covers/61dda0036226ab4f74bef9f2/03174703-87de-44b0-a620-f07520f08d66.png" alt="" style="display:block;margin:0 auto" />

<p>Here, q is the ratio between the stars' individual velocities and the escape velocity (q = V/Vesc). Where q ∈ [0,1] and g(q) ∈ [0,0.1]. To find a fit to this PDF, there are generally two directions you can take - rejection sampling, or the Newton root-finding method. I will be using the former. To implement Von Neumann rejection sampling, I sample 2 uniformly random numbers x and y in the range [0,1]. For each pair, if 0.01*y &lt; g(x), I accept q = x.</p>
<p>Since velocities are isotropic (same magnitude in all directions) I use the same method for the positions to assign random directions to the velocities.</p>
<p>To implement this, I extend my <code>plummer_sphere()</code> function as such:</p>
<pre><code class="language-plaintext"> v_esc = np.sqrt(2 * G * M / np.sqrt(r**2 + a**2))
 v_mag = 0
 q = 0.0

while True:
    q = np.random.uniform(0, 1)

    g_q = 0.1 * np.random.uniform(0, 1)
    if g_q &lt; (q**2 * (1 - q**2)**3.5):
        v_mag = q * v_esc
        break

#random direction for velocity vector just like the coordiantes
v_theta = np.arccos(np.random.uniform(-1, 1))
v_phi = np.random.uniform(0, 2 * np.pi)

vx = v_mag * np.sin(v_theta) * np.cos(v_phi)
vy = v_mag * np.sin(v_theta) * np.sin(v_phi)
vz = v_mag * np.cos(v_theta)

return [[x,y,z], [vx,vy,vz], Xi]
</code></pre>
<h3>Making The Simulation</h3>
<p>At this point, we still haven’t actually <em>created</em> the stars; but, we can do so by making an N number of objects of class Star and adding them to <code>stars[]</code>, as such :</p>
<pre><code class="language-python">for _ in range(N):
    data = plummer_sphere() 
    rt = vector(*map(float, data[0]))
    v = vector(*map(float, data[1]))
    m = float(data[2])
    stars.append(Star(rt, v, m))
</code></pre>
<p>Now that we’ve created and displayed our stars, we can move on to the movement logic. Firstly, we’ll need to define some variables to measure time, as shown below:</p>
<pre><code class="language-python">t = 0 #initial time
dt = 0.01 #small time step
</code></pre>
<p>Then, in a while loop, we can simulate the passage of time by increasing <code>t</code> by <code>dt</code> , and calculate the total force, and update the velocity and position of each star at each time interval to create a smooth animation.</p>
<pre><code class="language-python">while t &lt; 10:
    rate(100)
    
    #Update positions
    for star in stars:
        star.update_vel(dt)
    for star in stars:
        star.update_position(dt)
    calc_forces()
    for star in stars:
        star.update_vel(dt)

    t += dt
</code></pre>
<p>Running the code we have so far does not result in an animation just yet. This is because we haven’t updated the positions of the stars at all, we’ve just calculated the forces on them. We can fix this by adding a function <code>update_position()</code> to our Star class, which updates the position at a full time-step (dt) using the formula dx = vdt (since dx/dt = v).</p>
<pre><code class="language-python">def update_position(self, dt):
        self.pos += self.v * dt 
        self.sphere.pos = self.pos
</code></pre>
<p>For the velocities, we create a similar function. However, there is one important distinction (other than the formula, of course):</p>
<pre><code class="language-python">def update_vel(self,dt):
        self.v += self.a * dt/2 #v = u + at
</code></pre>
<p>Notice that instead of using dt, we're using dt/2. i.e, we're updating the velocity at a half time-step. The reason for this is to implement <strong>leapfrog integration</strong>. The leapfrog method solves equations of motion with a second order accuracy (which is why it is preferred over the commonly used Euler's integration), by calculating velocity and position at staggered time points.</p>
<p>In our loop, first, the velocity is calculated at a half time-step, dt/2, followed by the position calculated at a full time-step dt, i.e, x and v are calculated such that they 'leapfrog' over each other. This makes our calculations more precise, although a bit slower.</p>
<img src="https://cloudmate-test.s3.us-east-1.amazonaws.com/uploads/covers/61dda0036226ab4f74bef9f2/c19f3989-34cc-487f-be11-165f6d024bd3.png" alt="" style="display:block;margin:0 auto" />

<p>…and now you have a simplified n-body simulation! By tweaking the initial settings such as velocity, position, and mass, you can observe how even small changes can result in vastly different outcomes, demonstrating the chaotic behavior we discussed earlier.</p>
<p>The full program can be accessed at :</p>
<p><a href="https://github.com/fa22991/n-body-simulation-VPython/">https://github.com/fa22991/n-body-simulation-VPython/</a></p>
<p>Thank you for making it to the end!</p>
]]></content:encoded></item><item><title><![CDATA[Building a fantasy top-down game in Python (Part 4)]]></title><description><![CDATA[To recap, we now have

A map layout with hazard, enemy, player and coin components

Character animations

A player that can move and attack

Enemies that can chase and attack the player


At this point, we could integrate more complex pathfinding alg...]]></description><link>https://webwidewit.com/building-a-fantasy-top-down-game-in-python-part-4</link><guid isPermaLink="true">https://webwidewit.com/building-a-fantasy-top-down-game-in-python-part-4</guid><category><![CDATA[Python 3]]></category><category><![CDATA[pygame]]></category><category><![CDATA[coding]]></category><category><![CDATA[coding journey]]></category><dc:creator><![CDATA[Fatima Ali]]></dc:creator><pubDate>Fri, 01 Aug 2025 05:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1761431265630/63d362d2-eb06-4c59-80b7-d38049def10b.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>To recap, we now have</p>
<ul>
<li><p>A map layout with hazard, enemy, player and coin components</p>
</li>
<li><p>Character animations</p>
</li>
<li><p>A player that can move and attack</p>
</li>
<li><p>Enemies that can chase and attack the player</p>
</li>
</ul>
<p>At this point, we could integrate more complex pathfinding algorithms such as A* or Dijkstra’s for the enemies, but in a game as small as this one I just didn’t see a point in doing that (a.k.a I’m very lazy and simply did not want to). But to wrap it up, we need to make some finishing touches. A good game needs a start screen, a help manual, and an ending based on success or failure.</p>
<p>Let’s start with the ‘start’ screen. First, we need to keep track of what part of the game the player is in: Have they started? Did they finish? Are they stuck?<br />When you have a problem like this, where you need to keep track of a state across multiple files, its best to create a variable in settings.py that we can import everywhere. In this case, I made 4 variables : whichever one of them is true, gets displayed. If none are true, it means the game has started. Now you might wonder, would it not have been easier to make one variable and set it to either ‘intro’, ‘help’, ‘success’ or ‘failure’? The answer would be yes, you absolutely can, I just didn’t think of that when I made this and I don’t care enough to optimize it now.</p>
<pre><code class="lang-python">in_intro = <span class="hljs-literal">True</span> <span class="hljs-comment">#we start in the intro</span>
in_help = <span class="hljs-literal">False</span>
success = <span class="hljs-literal">False</span>
failure = <span class="hljs-literal">False</span>
</code></pre>
<p>One thing to note is that when you import a data structure from a different file, your local file creates a copy of it and edits the copy instead of the original. If we keep editing the local copies, our changes wouldn’t be reflected throughout the project. To fix this, instead of using <code>from settings import *</code>, we say <code>import settings</code> and then use <code>settings.variable_name</code> , which is the original variable, throughout our project.</p>
<p>We can then write a function to display the intro screen as such</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">intro_screen</span>(<span class="hljs-params">self, clicked</span>):</span>
        bg = transform.scale(image.load(<span class="hljs-string">'assets//screens//bg.jpg'</span>), (<span class="hljs-number">1000</span>,<span class="hljs-number">500</span>)) <span class="hljs-comment"># did not design the bg</span>
        banner = image.load(<span class="hljs-string">'assets//screens//banner.png'</span>).convert_alpha() <span class="hljs-comment">#i designed the banner, fun fact, I styled it exactly like the actual Geronimo stilton book title</span>
        button = transform.scale(image.load(<span class="hljs-string">'assets//screens//button.png'</span>), (<span class="hljs-number">300</span>, <span class="hljs-number">50</span>))
        self.screen.blit(bg, (<span class="hljs-number">0</span>,<span class="hljs-number">0</span>))
        self.screen.blit(banner, (<span class="hljs-number">0</span>,<span class="hljs-number">50</span>))
        self.screen.blit(button, (<span class="hljs-number">400</span>,<span class="hljs-number">250</span>))
        col_0 = <span class="hljs-string">"#312400"</span>
        col_1 = <span class="hljs-string">"#312400"</span>
        col_2 = <span class="hljs-string">"#312400"</span>
        game_font = font.Font(<span class="hljs-string">"assets\\screens\\Pixellari.ttf"</span>, <span class="hljs-number">30</span>)
        sb_rect = Rect(<span class="hljs-number">520</span>,<span class="hljs-number">260</span>, <span class="hljs-number">150</span>, <span class="hljs-number">50</span>)
        self.screen.blit(button, (<span class="hljs-number">400</span>,<span class="hljs-number">310</span>))
        h_rect = Rect(<span class="hljs-number">520</span>,<span class="hljs-number">320</span>, <span class="hljs-number">150</span>, <span class="hljs-number">50</span>)
        self.screen.blit(button, (<span class="hljs-number">400</span>,<span class="hljs-number">370</span>))
        q_rect = Rect(<span class="hljs-number">520</span>,<span class="hljs-number">380</span>, <span class="hljs-number">150</span>, <span class="hljs-number">50</span>)
        buttons = [q_rect, sb_rect, h_rect]
        l,m,r = mouse.get_pressed()
        pos = mouse.get_pos()
        <span class="hljs-keyword">if</span> clicked: 
            <span class="hljs-keyword">for</span> i, button <span class="hljs-keyword">in</span> enumerate(buttons): 
                <span class="hljs-keyword">if</span> button.collidepoint(pos):
                    <span class="hljs-keyword">if</span> i == <span class="hljs-number">0</span>:
                        col_0 = <span class="hljs-string">"#966825"</span>
                        <span class="hljs-keyword">return</span> <span class="hljs-string">'quit'</span>
                    <span class="hljs-keyword">elif</span> i == <span class="hljs-number">1</span>:
                        col_1 = <span class="hljs-string">"#966825"</span>
                        <span class="hljs-keyword">return</span> <span class="hljs-string">'start'</span>
                    <span class="hljs-keyword">else</span>: 
                        col_2 = <span class="hljs-string">"#966825"</span>
                        <span class="hljs-keyword">return</span> <span class="hljs-string">"help"</span>
        quit_button = game_font.render(<span class="hljs-string">"Quit"</span>, <span class="hljs-literal">True</span>, col_0)
        start_button = game_font.render(<span class="hljs-string">"Start"</span>, <span class="hljs-literal">True</span>, col_1)
        help_button = game_font.render(<span class="hljs-string">"Help"</span>, <span class="hljs-literal">True</span>, col_2)
        self.screen.blit(start_button, sb_rect)
        self.screen.blit(help_button, h_rect)
        self.screen.blit(quit_button, q_rect)
        <span class="hljs-keyword">return</span> <span class="hljs-literal">None</span>
</code></pre>
<p>I will link all the assets used here below, but basically what this does is creates a screen with 3 buttons (rects that do something when clicked) - start, help and quit, and return the same depending on which one is clicked.</p>
<p>The help screen is similar, except the only button it has is a back button which goes back to either the intro screen, or the game depending on whether or not the player has started.</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">show_help_screen</span>(<span class="hljs-params">self,clicked</span>):</span>
        self.screen.fill(<span class="hljs-string">'white'</span>)
        help = transform.scale(image.load(<span class="hljs-string">'files\\assets//screens//help screen.png'</span>), (<span class="hljs-number">1000</span>,<span class="hljs-number">500</span>))
        self.screen.blit(help, (<span class="hljs-number">0</span>,<span class="hljs-number">10</span>))
        back = transform.scale(image.load(<span class="hljs-string">'files\\assets//screens//back.png'</span>), (<span class="hljs-number">100</span>,<span class="hljs-number">60</span>))
        back_rect = self.screen.blit(back, (<span class="hljs-number">450</span>,<span class="hljs-number">0</span>))
        back.get_rect()
        pos = mouse.get_pos()
        <span class="hljs-keyword">if</span> clicked:
            <span class="hljs-keyword">if</span> back_rect.collidepoint(pos):
                <span class="hljs-keyword">return</span> <span class="hljs-string">'back'</span>
        <span class="hljs-keyword">return</span>
</code></pre>
<p>Lastly, the success/failure screens will be the same except for their backgrounds (I put the text on the backgrounds itself so I wouldn’t have to render text in pygame).</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">success_failure_screen</span>(<span class="hljs-params">self, clicked, img</span>):</span>
        bg = transform.scale(image.load(img), (<span class="hljs-number">1000</span>,<span class="hljs-number">500</span>)) <span class="hljs-comment"># did not design the bg</span>
        button = transform.scale(image.load(<span class="hljs-string">'files\\assets//screens//button.png'</span>), (<span class="hljs-number">300</span>, <span class="hljs-number">50</span>))
        self.screen.blit(bg, (<span class="hljs-number">0</span>,<span class="hljs-number">0</span>))
        self.screen.blit(button, (<span class="hljs-number">370</span>,<span class="hljs-number">250</span>))
        col_0 = <span class="hljs-string">"#312400"</span>
        col_1 = <span class="hljs-string">"#312400"</span>
        col_2 = <span class="hljs-string">"#312400"</span>
        game_font = font.Font(<span class="hljs-string">"files\\assets\\screens\\Pixellari.ttf"</span>, <span class="hljs-number">30</span>)
        sb_rect = Rect(<span class="hljs-number">470</span>,<span class="hljs-number">260</span>, <span class="hljs-number">150</span>, <span class="hljs-number">50</span>)
        self.screen.blit(button, (<span class="hljs-number">370</span>,<span class="hljs-number">310</span>))
        h_rect = Rect(<span class="hljs-number">490</span>,<span class="hljs-number">320</span>, <span class="hljs-number">150</span>, <span class="hljs-number">50</span>)
        self.screen.blit(button, (<span class="hljs-number">370</span>,<span class="hljs-number">370</span>))
        q_rect = Rect(<span class="hljs-number">490</span>,<span class="hljs-number">380</span>, <span class="hljs-number">150</span>, <span class="hljs-number">50</span>)
        buttons = [q_rect, sb_rect, h_rect]
        l,m,r = mouse.get_pressed()
        pos = mouse.get_pos()
        <span class="hljs-keyword">if</span> clicked: 
            <span class="hljs-keyword">for</span> i, button <span class="hljs-keyword">in</span> enumerate(buttons): 
                <span class="hljs-keyword">if</span> button.collidepoint(pos):
                    <span class="hljs-keyword">if</span> i == <span class="hljs-number">0</span>:
                        col_0 = <span class="hljs-string">"#966825"</span>
                        <span class="hljs-keyword">return</span> <span class="hljs-string">'quit'</span>
                    <span class="hljs-keyword">elif</span> i == <span class="hljs-number">1</span>:
                        col_1 = <span class="hljs-string">"#966825"</span>
                        <span class="hljs-keyword">return</span> <span class="hljs-string">'start'</span>
                    <span class="hljs-keyword">else</span>: 
                        col_2 = <span class="hljs-string">"#966825"</span>
                        <span class="hljs-keyword">return</span> <span class="hljs-string">"help"</span>
        quit_button = game_font.render(<span class="hljs-string">"Quit"</span>, <span class="hljs-literal">True</span>, col_0)
        start_button = game_font.render(<span class="hljs-string">"Restart"</span>, <span class="hljs-literal">True</span>, col_1)
        help_button = game_font.render(<span class="hljs-string">"Help"</span>, <span class="hljs-literal">True</span>, col_2)
        self.screen.blit(start_button, sb_rect)
        self.screen.blit(help_button, h_rect)
        self.screen.blit(quit_button, q_rect)
        <span class="hljs-keyword">return</span> <span class="hljs-literal">None</span>
</code></pre>
<p>These functions don’t actually DO anything just yet because, well, we haven’t called them yet! In our main file’s run function, we need to add a couple of conditionals to decide which screen to render. But before everything, we need to make another variable called ‘clicked’ in settings.py and import it the same way we imported the other four variables. What this variable does is detects whenever the player clicks on the screen by turning True or False. Now in our main file’s run function, we can set <code>settings.clicked = False</code> so that it resets each time. Then, in the event loop, we check for the event of <code>MOUSEBUTTONDOWN</code> and set <code>settings.clicked = True</code>. We can then create a chain of conditionals :  </p>
<pre><code class="lang-python">
<span class="hljs-keyword">if</span> self.in_intro:
   <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> mixer.music.get_busy():
      mixer.music.play(<span class="hljs-number">-1</span>) <span class="hljs-comment">#optional, starting music that i loaded at the beginning</span>
   action = self.intro_screen(files.settings.clicked) <span class="hljs-comment">#stores start, help or quit based on what self.intro_screen() returns</span>
   <span class="hljs-keyword">if</span> action == <span class="hljs-string">"start"</span>:
      self.in_intro = <span class="hljs-literal">False</span>
      mixer.music.stop()
   <span class="hljs-keyword">elif</span> action == <span class="hljs-string">"quit"</span>:
      quit()
      sys.exit()
   <span class="hljs-keyword">elif</span> action == <span class="hljs-string">"help"</span>:
      self.in_intro = <span class="hljs-literal">False</span>
      in_help = <span class="hljs-literal">True</span>
      mixer.music.stop()
</code></pre>
<p>The conditionals for help and success/failure will be sort of similar :</p>
<pre><code class="lang-python">          <span class="hljs-keyword">elif</span> in_help:
                action = self.show_help_screen(files.settings.clicked)
                <span class="hljs-keyword">if</span> action == <span class="hljs-string">'back'</span>:
                    in_help = <span class="hljs-literal">False</span>
                    <span class="hljs-keyword">if</span> self.was_playing:
                        self.was_playing = <span class="hljs-literal">False</span>  <span class="hljs-comment"># reset it</span>
                        <span class="hljs-comment"># resume level</span>
                        <span class="hljs-keyword">continue</span>  <span class="hljs-comment"># next loop will rerun whichever screen it was on </span>
                    <span class="hljs-keyword">else</span>:
                        self.in_intro = <span class="hljs-literal">True</span>
            <span class="hljs-keyword">else</span>:
                <span class="hljs-keyword">if</span> files.settings.success: <span class="hljs-comment"># im using files.settings.success instead of success because i dont want to check the local copy, i want to check the global copy in the actual file</span>
                    action = self.success_failure_screen(files.settings.clicked, <span class="hljs-string">'files\\assets//screens//Success screen.png'</span>)
                    triumph = mixer.Sound(<span class="hljs-string">'files\\assets\\sounds\\11l-victory_trumpet-1749704469779-358762.mp3'</span>)
                    <span class="hljs-keyword">if</span> self.sound_played == <span class="hljs-literal">False</span>:
                        triumph.play(<span class="hljs-number">1</span>)
                        self.sound_played = <span class="hljs-literal">True</span>

                    <span class="hljs-keyword">if</span> action == <span class="hljs-string">"start"</span>:
                        files.settings.success = <span class="hljs-literal">False</span>
                        self.level = Level(self.screen)
                        self.sound_played = <span class="hljs-literal">False</span>
                        self.in_intro = <span class="hljs-literal">False</span>

                    <span class="hljs-keyword">elif</span> action == <span class="hljs-string">"quit"</span>:
                        quit()
                        sys.exit()

                    <span class="hljs-keyword">elif</span> action == <span class="hljs-string">"help"</span>:
                        self.in_intro = <span class="hljs-literal">False</span>
                        self.was_playing = <span class="hljs-literal">True</span>
                        in_help = <span class="hljs-literal">True</span>

                <span class="hljs-keyword">elif</span> files.settings.failure:
                    action = self.success_failure_screen(files.settings.clicked, <span class="hljs-string">'files\\assets//screens//Failure screen.png'</span>)
                    failure = mixer.Sound(<span class="hljs-string">'files\\assets\\sounds\\losing-horn-313723.mp3'</span>)
                    <span class="hljs-keyword">if</span> self.sound_played == <span class="hljs-literal">False</span>:
                        failure.play(<span class="hljs-number">1</span>)
                        self.sound_played = <span class="hljs-literal">True</span>
                    <span class="hljs-keyword">if</span> action == <span class="hljs-string">"start"</span>:
                        files.settings.failure = <span class="hljs-literal">False</span>
                        self.level = Level(self.screen)
                        self.sound_played = <span class="hljs-literal">False</span>
                        self.in_intro = <span class="hljs-literal">False</span>
                    <span class="hljs-keyword">elif</span> action == <span class="hljs-string">"quit"</span>:
                        quit()
                        sys.exit()
                    <span class="hljs-keyword">elif</span> action == <span class="hljs-string">"help"</span>:
                        self.in_intro = <span class="hljs-literal">False</span>
                        in_help = <span class="hljs-literal">True</span>
                        self.was_playing = <span class="hljs-literal">True</span>
                <span class="hljs-keyword">else</span>:
                    check_for_help = self.level.run() <span class="hljs-comment">#tweak the run function in level to return help if we clicked help there</span>
                    <span class="hljs-keyword">if</span> check_for_help == <span class="hljs-string">'help'</span>:
                        self.was_playing = <span class="hljs-literal">True</span>
                        in_help = <span class="hljs-literal">True</span>
</code></pre>
<p>For this section, I made another class variable called <code>was_playing</code> to check if the game was already in session when the player clicked on the help manual (they might need help during the game) or whether they were on the intro screen. Based off of this, we choose which screen to re-render. Now, another problem I ran into was when rendering the success/failure screen, to be more specific, restarting the game once the player finished. I realized that simply setting success/failure = False did not start a NEW game. To do that, we create a new Level object instead which creates a new game afresh.</p>
<p>One problem in this game is that we don’t really let the player know how they’re doing, as in the player is unaware of how much health, points, coins etc. they have. We can add a small section on top of the screen during the game to display these stats since we’re already keeping track of them in our game. To do this, I made another class (the last one I swear) called UI.</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UI</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self</span>):</span>
        self.screen = display.get_surface()
        self.font = font.Font(<span class="hljs-string">'files//assets//screens//Pixellari.ttf'</span>, <span class="hljs-number">20</span>)
        self.health_bar = Rect(<span class="hljs-number">115</span>,<span class="hljs-number">10</span>, h_width, h_height)
        self.h_col = <span class="hljs-string">"#F3E73F"</span>


    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">display</span>(<span class="hljs-params">self, player</span>):</span>
        dark_overlay = Surface((<span class="hljs-number">350</span>,<span class="hljs-number">220</span>), SRCALPHA)
        dark_overlay.fill((<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">100</span>))
        self.screen.blit(dark_overlay, (<span class="hljs-number">0</span>,<span class="hljs-number">0</span>))
</code></pre>
<p>Right now, display only creates the transparent surface to put the stats on, but I added a bunch of code to create the health bar, display the score, current spells, magical objects collected etc.</p>
<h3 id="heading-ia"> </h3>
<p>Conclusion</p>
<p><a target="_blank" href="https://github.com/fa22991/Python-top-down-game.git">https://github.com/fa22991/Python-top-down-game.git</a></p>
<p>Above is the link to the repo (for some reason the only way I could upload it was as a zip file).<br />Thank you for following along till the end!</p>
]]></content:encoded></item><item><title><![CDATA[Building a fantasy top-down game in Python (Part 3)]]></title><description><![CDATA[Making the Enemy class
Okay, so now that we’ve made a good chunk of our game, we can finally move on to making our enemies. Its always a good idea to start by outlining what you want to accomplish before writing any code. For the enemies, I wanted th...]]></description><link>https://webwidewit.com/building-a-fantasy-top-down-game-in-python-part-3</link><guid isPermaLink="true">https://webwidewit.com/building-a-fantasy-top-down-game-in-python-part-3</guid><category><![CDATA[Python]]></category><category><![CDATA[Game Development]]></category><category><![CDATA[coding]]></category><category><![CDATA[Object Oriented Programming]]></category><dc:creator><![CDATA[Fatima Ali]]></dc:creator><pubDate>Tue, 01 Jul 2025 05:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1759608866993/e896bd03-3151-4304-ba9f-77bb8deb6777.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3 id="heading-making-the-enemy-class">Making the Enemy class</h3>
<p>Okay, so now that we’ve made a good chunk of our game, we can finally move on to making our enemies. Its always a good idea to start by outlining what you want to accomplish before writing any code. For the enemies, I wanted there to be 3 different types of fairies, each of which would have different attacks, damage, cooldowns and speeds, just to make the game less repetitive.</p>
<p>Let’s start by outlining these different parameters in <code>settings.py</code>, a file which we can import anywhere in our project to gain access to these parameters without creating many copies of the same data structure.</p>
<pre><code class="lang-python">enemy_data = {
    <span class="hljs-string">'Fairy 1'</span> : {<span class="hljs-string">'health'</span>: <span class="hljs-number">100</span>, <span class="hljs-string">'damage'</span>: <span class="hljs-number">5</span>, <span class="hljs-string">'attack_r'</span> : <span class="hljs-number">100</span>, <span class="hljs-string">'notice_r'</span> : <span class="hljs-number">200</span>, <span class="hljs-string">'resistance'</span> : <span class="hljs-number">10</span>, <span class="hljs-string">'speed'</span>:<span class="hljs-number">5</span>, <span class="hljs-string">'cooldown'</span> : <span class="hljs-number">80</span>},
    <span class="hljs-string">'Fairy 2'</span> : {<span class="hljs-string">'health'</span>: <span class="hljs-number">100</span>, <span class="hljs-string">'damage'</span>: <span class="hljs-number">3</span>, <span class="hljs-string">'attack_r'</span> : <span class="hljs-number">80</span>,<span class="hljs-string">'notice_r'</span> : <span class="hljs-number">300</span>, <span class="hljs-string">'resistance'</span> : <span class="hljs-number">25</span>, <span class="hljs-string">'speed'</span>: <span class="hljs-number">8</span>, <span class="hljs-string">'cooldown'</span> : <span class="hljs-number">50</span>},
    <span class="hljs-string">'Fairy 3'</span> : {<span class="hljs-string">'health'</span>: <span class="hljs-number">100</span>, <span class="hljs-string">'damage'</span>: <span class="hljs-number">10</span>, <span class="hljs-string">'attack_r'</span> : <span class="hljs-number">50</span>, <span class="hljs-string">'notice_r'</span> : <span class="hljs-number">100</span>, <span class="hljs-string">'resistance'</span> : <span class="hljs-number">30</span>,<span class="hljs-string">'speed'</span>: <span class="hljs-number">6</span>, <span class="hljs-string">'cooldown'</span> : <span class="hljs-number">100</span>}
}
</code></pre>
<p>Each fairy begins with 100% health, which will decrease in accordance to player attacks. ‘damage’ specifies how much the player’s health will decrease when the enemy attacks it. ‘attack_r’ and ‘notice_r’ are the attack and notice radius respectively, a.k.a, the distances from the player at which the fairies can attack, and ‘notice’ (chase) the player. ‘resistance’ is how much the fairies will sort of <em>bounce back</em> when attacked by the player. ‘speed’ is self-explanatory and cooldown is the amount of time after which the fairies can re-cast their attacks.</p>
<p>The base of the enemy class should look something like this, following the general ‘Tile’ template we used for everything else :</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> pygame <span class="hljs-keyword">import</span> *
<span class="hljs-keyword">from</span> settings <span class="hljs-keyword">import</span> *
<span class="hljs-keyword">from</span> math <span class="hljs-keyword">import</span> *

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Enemy</span>(<span class="hljs-params">sprite.Sprite</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, pos, groups, type, path, player, obstacles</span>):</span>
        super().__init__(groups) <span class="hljs-comment">#adds the sprite to the given groups</span>
        self.sprite_type = type <span class="hljs-comment">#when creating the map, I gave each entity a certain class. enemy sprites under "enemy", gems under "collectible" etc</span>
        self.current_sprite = <span class="hljs-number">0.0</span> <span class="hljs-comment">#to keep track of the animation</span>
        self.enemy_type = path.split(<span class="hljs-string">'\\'</span>)[<span class="hljs-number">3</span>].split(<span class="hljs-string">'.'</span>)[<span class="hljs-number">0</span>]
        self.speed = enemy_data[self.enemy_type][<span class="hljs-string">'speed'</span>]
        self.health = enemy_data[self.enemy_type][<span class="hljs-string">'health'</span>]
        self.resistance = enemy_data[self.enemy_type][<span class="hljs-string">'resistance'</span>]
        self.attack_r = enemy_data[self.enemy_type][<span class="hljs-string">'attack_r'</span>]
        self.notice_r = enemy_data[self.enemy_type][<span class="hljs-string">'notice_r'</span>]
        self.damage = enemy_data[self.enemy_type][<span class="hljs-string">'damage'</span>]
        self.cooldown = enemy_data[self.enemy_type][<span class="hljs-string">'cooldown'</span>]
        self.status = <span class="hljs-string">'idle'</span>
        self.direction = math.Vector2()
        self.image = image.load(path)
        self.rect = self.image.get_rect(topleft = pos)
        self.mask = mask.from_surface(self.image)
        self.player = player <span class="hljs-comment">#passing in an instance of the player to get its position and stuff</span>
        self.attack_timer = <span class="hljs-number">0</span> <span class="hljs-comment">#this'll make sense later</span>
        self.last_hit = <span class="hljs-number">0</span> <span class="hljs-comment">#this too</span>
</code></pre>
<p><code>self.enemy_type</code> is the type of fairy (1,2 or 3) extracted from the path to the image passed into the enemy object. We then use this variable to access the correct type of fairy’s information (speed, damage etc.) from <code>enemy_data</code>, the list we imported from <code>settings.py</code>. You’ll recognize that most of the class variables match our player class, and that’s because they’re essentially the same <em>type</em> of thing, but perform a different role.</p>
<p>My images for the enemies were in the form of spritesheets, so I used the same <code>load_sprite_sheet</code> function as the player class to load them in. The animation function is a bit different here though, since there isn’t a whole new spritesheet to render when the enemy is attacking (we’ll ignore attacks right now and work on them later in our projectile class) :</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">animate</span>(<span class="hljs-params">self, dir</span>):</span>
        sheet = <span class="hljs-string">f"levels//level_1//tilesets//<span class="hljs-subst">{self.enemy_type}</span>_spritesheet.png"</span>
        sprites = self.load_sprite_sheet(sheet, <span class="hljs-number">32</span>, <span class="hljs-number">64</span>)
        self.current_sprite += <span class="hljs-number">0.15</span>
        <span class="hljs-keyword">if</span> self.current_sprite &gt;= <span class="hljs-number">3</span>:
            self.current_sprite = <span class="hljs-number">0</span>
        <span class="hljs-keyword">if</span> dir == <span class="hljs-string">'right'</span>:
            self.image = sprites[int(self.current_sprite)] <span class="hljs-comment"># if the player is towards the right of the player, </span>
                                                           <span class="hljs-comment"># the enemies will face right</span>
        <span class="hljs-keyword">else</span>:
            self.image = transform.flip(sprites[int(self.current_sprite)], <span class="hljs-literal">True</span>, <span class="hljs-literal">False</span>) <span class="hljs-comment">#if not, we flip the enemies</span>
                                                                                        <span class="hljs-comment">#so that they face left</span>
        self.mask = mask.from_surface(self.image)
</code></pre>
<p>The logic behind the animation is still the same as our <code>Player</code> and <code>Hazard</code> classes. Enemy movement is a bit different however, since they can go over obstacles that the player cannot.</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">move</span>(<span class="hljs-params">self, direction</span>):</span>
        <span class="hljs-comment">#vector subtraction. V (direction) = VB (player) - VA (enemy)</span>
        old_rect = self.rect.copy()
        <span class="hljs-keyword">if</span> direction.length() != <span class="hljs-number">0</span>: <span class="hljs-comment">#if the distance between them is not 0</span>
            self.direction = direction.normalize()
        <span class="hljs-keyword">else</span>:
            self.direction = math.Vector2(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>)

        self.trymove(<span class="hljs-string">'h'</span>, self.direction.x*self.speed, old_rect)
        self.trymove(<span class="hljs-string">'v'</span>, self.direction.y*self.speed, old_rect)
</code></pre>
<p><code>move()</code> calls <code>trymove()</code> in both the horizontal and vertical directions, passing in a velocity vector and the old position of the enemy as arguments. I realized that simply moving the enemies in the direction of the player wasn’t the best idea, because the fairies ended up overlapping with each other and moving towards the player as a single entity. As a solution, I created a function <code>trymove()</code> which checks for enemy-enemy collisions and separates them.</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">trymove</span>(<span class="hljs-params">self,dir, val, old_rect</span>):</span>
        copy = sprite.Group() <span class="hljs-comment"># i dont know why but copy = self.obstacles.copy() wasnt working so i had to add its sprites to copy using add</span>
        copy.add(self.obstacles)
        copy.remove(self) <span class="hljs-comment"># remove self from the copy and check if it collides with any other fairies</span>

        <span class="hljs-keyword">if</span> dir == <span class="hljs-string">"h"</span>:
            <span class="hljs-keyword">if</span> sprite.spritecollideany(self, copy, sprite.collide_mask) == <span class="hljs-literal">None</span>:
                self.rect.x += val

            <span class="hljs-keyword">for</span> spr <span class="hljs-keyword">in</span> sprite.spritecollide(self, copy, <span class="hljs-literal">False</span>, sprite.collide_mask):

                <span class="hljs-keyword">if</span> spr.sprite_type == <span class="hljs-string">'enemy'</span>: <span class="hljs-comment">#if two sprites overlap, move them a little so they dont overlap</span>
                    offset = self.rect.centerx - spr.rect.centerx, self.rect.centery - spr.rect.centery
                    distance = hypot(*offset)
                    <span class="hljs-keyword">if</span> distance &lt; <span class="hljs-number">32</span> <span class="hljs-keyword">and</span> math.Vector2(offset).length != <span class="hljs-number">0</span>: <span class="hljs-comment">#if distance between their centers is less than half a tile which means they're in the same tile</span>
                        push_vector = math.Vector2(offset).normalize() * <span class="hljs-number">5</span> 
                        self.rect.x += push_vector.x 
                        self.rect.y += push_vector.y
                <span class="hljs-keyword">else</span>: <span class="hljs-comment">#we don't care about collision with obstacles like trees, rocks etc.</span>
                    self.rect.x += val

        <span class="hljs-keyword">if</span> dir == <span class="hljs-string">"v"</span>:
            <span class="hljs-keyword">if</span> sprite.spritecollideany(self, copy, sprite.collide_mask) == <span class="hljs-literal">None</span>:
                self.rect.y += val
            <span class="hljs-keyword">for</span> spr <span class="hljs-keyword">in</span> sprite.spritecollide(self, copy, <span class="hljs-literal">False</span>, sprite.collide_mask): 
                <span class="hljs-keyword">if</span> spr.sprite_type == <span class="hljs-string">'enemy'</span>: <span class="hljs-comment">#if two sprites overlap, move them a little so they dont overlap</span>
                    offset = self.rect.centerx - spr.rect.centerx, self.rect.centery - spr.rect.centery
                    distance = hypot(*offset)
                    <span class="hljs-keyword">if</span> distance &lt; <span class="hljs-number">32</span> <span class="hljs-keyword">and</span> math.Vector2(offset).length != <span class="hljs-number">0</span>: <span class="hljs-comment">#if distance between their centers is less than half a tile which means they're in the same tile</span>
                        push_vector = math.Vector2(offset).normalize() * <span class="hljs-number">5</span> 
                        self.rect.x += push_vector.x 
                        self.rect.y += push_vector.y
                <span class="hljs-keyword">else</span>: <span class="hljs-comment">#we don't care about collision with obstacles like trees, rocks etc.</span>
                    self.rect.y += val
</code></pre>
<h3 id="heading-creating-the-projectile-class-to-handle-enemy-attacks">Creating the ‘Projectile’ class to handle enemy attacks</h3>
<p>Okay, so now that we have animated enemies that move towards the player without overlapping, we can move on to making enemy attacks! To keep the enemy class nice and short, I created a new class ‘Projectile’ to handle projectile attacks.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> pygame <span class="hljs-keyword">import</span> *
<span class="hljs-keyword">from</span> math <span class="hljs-keyword">import</span> *
<span class="hljs-keyword">from</span> settings <span class="hljs-keyword">import</span> *
<span class="hljs-keyword">import</span> glob <span class="hljs-comment">#I'll explain this later </span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Projectile</span>(<span class="hljs-params">sprite.Sprite</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, pos, direction, speed, groups, enemy_type, player, enemy_that_shot_projectile</span>):</span>
        super().__init__(groups)
        self.sprite_type = <span class="hljs-string">"projectile"</span> <span class="hljs-comment">#remember we need this when detecting collisions</span>
        self.enemy_type = enemy_type <span class="hljs-comment">#the type of enemy that shot the projectile, Fairy 1, Fairy 2 etc.</span>
        self.image = Surface((<span class="hljs-number">10</span>, <span class="hljs-number">10</span>), SRCALPHA)
        self.rect = self.image.get_rect(center=pos)
        self.mask = mask.from_surface(self.image)
        self.cooldown = enemy_data[enemy_type][<span class="hljs-string">'cooldown'</span>]
        self.direction = direction.normalize()
        self.speed = enemy_data[self.enemy_type][<span class="hljs-string">'speed'</span>]
        self.lifetime = <span class="hljs-number">30</span>  <span class="hljs-comment"># Frames until it disappears so like 1/2s frame at 60 FPS</span>
        self.screen = display.get_surface()
        self.current_sprite = <span class="hljs-number">0</span> 
        self.player = player
        self.attackable = groups[<span class="hljs-number">1</span>] <span class="hljs-comment">#self.attackable (I'll discuss what this is</span>
        self.enemy_that_shot_projectile = enemy_that_shot_projectile <span class="hljs-comment">#a copy of the enemy object that shot the projectile</span>
</code></pre>
<p>Again, the projectile class is just another spin on the ‘Tile’ class template. The class variables that are new to this class have comments outlining their purpose. For this to work, we need to create a new group called ‘attackable’, which will include the enemies and their projectiles; these are as the name suggests, things the player can <em>attack</em>. Let’s head back to our map loader class and replace the part where we create the ‘enemies’ layer with the following code :</p>
<pre><code class="lang-python"><span class="hljs-keyword">if</span> layer == <span class="hljs-string">"enemies"</span>:
    <span class="hljs-keyword">if</span> col <span class="hljs-keyword">in</span> self.tileset.keys():
         <span class="hljs-comment">#y + 64-16 because the images are 16x16px not 64x64</span>
       enemy = Enemy((x,y+<span class="hljs-number">52</span>), [self.visible, self.obstacles, self.attackable], <span class="hljs-string">'enemy'</span>,self.tileset[col], self.player, self.obstacles)
       enemy.projectile_group = [self.visible, self.attackable]
</code></pre>
<p>This initializes an object of the Enemy class and adds it to <code>self.attackable</code> as well as <code>self.obstacles</code> and <code>self.visible</code>. It also creates a class variable called ‘projectile_group’ that contains <code>self.visible</code> and <code>self.attackable</code>, which we’ll use when creating an object of the Projectile class.</p>
<h3 id="heading-fixing-our-player-class">Fixing our Player class</h3>
<p>One problem that I ran into when making this game was that if there was more than one enemy nearby and the player was attacking, regardless of which enemy the attack was directed at, it killed all of them. To fix this, I created a function within the Player class called ‘attack’, which finds the ‘target’ enemy, a.k.a the enemy at which the attack was targeted.</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">attack</span>(<span class="hljs-params">self, enemies</span>):</span>
        direction = self.current_state
        attack_range = <span class="hljs-number">100</span>
        target_enemy = <span class="hljs-literal">None</span>

        <span class="hljs-keyword">for</span> enemy <span class="hljs-keyword">in</span> enemies:
            <span class="hljs-keyword">if</span> enemy.sprite_type == <span class="hljs-string">"enemy"</span>:
                dx = enemy.rect.centerx - self.rect.centerx
                dy = enemy.rect.centery - self.rect.centery

                <span class="hljs-keyword">if</span> direction == <span class="hljs-string">"r"</span> <span class="hljs-keyword">and</span> <span class="hljs-number">0</span> &lt;= dx &lt;= attack_range <span class="hljs-keyword">and</span> abs(dy) &lt; <span class="hljs-number">32</span>: <span class="hljs-comment">#chooses the enemy in the most appropriate direction, right, left, top or down</span>
                    target_enemy = enemy

                <span class="hljs-keyword">elif</span> direction == <span class="hljs-string">"l"</span> <span class="hljs-keyword">and</span> -attack_range &lt;= dx &lt;= <span class="hljs-number">0</span> <span class="hljs-keyword">and</span> abs(dy) &lt; <span class="hljs-number">32</span>:
                    target_enemy = enemy

                <span class="hljs-keyword">elif</span> direction == <span class="hljs-string">"u"</span> <span class="hljs-keyword">and</span> -attack_range &lt;= dy &lt;= <span class="hljs-number">0</span> <span class="hljs-keyword">and</span> abs(dx) &lt; <span class="hljs-number">32</span>:
                    target_enemy = enemy

                <span class="hljs-keyword">elif</span> direction == <span class="hljs-string">"d"</span> <span class="hljs-keyword">and</span> <span class="hljs-number">0</span> &lt;= dy &lt;= attack_range <span class="hljs-keyword">and</span> abs(dx) &lt; <span class="hljs-number">32</span>:
                    target_enemy = enemy

                <span class="hljs-keyword">if</span> target_enemy:
                    <span class="hljs-keyword">break</span>  <span class="hljs-comment"># only one enemy</span>

        <span class="hljs-keyword">if</span> target_enemy:
            self.target = target_enemy
            target_enemy.rect.x += target_enemy.resistance
            target_enemy.rect.y += target_enemy.resistance
            target_enemy.health -= attack_data[self.weapon][<span class="hljs-string">'damage'</span>]
</code></pre>
<p>The parameter ‘enemies’ is a bit of a misnomer here since what we pass in is actually the entire obstacle group (that’s why we check the sprite type within the loop). The way to find the target enemy is quite simple, you just check which direction the player is facing and check which of the enemies lie within the same tile (&lt;32px) and the attack range (100px). Once the target enemy is found, you push it back by its resistance and decrease its health by the amount of damage of the weapon used. We can then call this function in the players update function by adding the lines :</p>
<pre><code class="lang-python">    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">update</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">global</span> attacked <span class="hljs-comment">#declare attacked as false at the beginning of the program</span>
        self.input()
        self.move()
        <span class="hljs-keyword">if</span> self.is_attacking:
            attacked = <span class="hljs-literal">True</span>
            self.attack(self.obstacles)
            <span class="hljs-keyword">if</span> time.get_ticks() - self.attack_time &gt; <span class="hljs-number">360</span>: <span class="hljs-comment">#360s is the cooldown</span>
                self.is_attacking = <span class="hljs-literal">False</span> <span class="hljs-comment">#we can attack again after 360s</span>
</code></pre>
<h3 id="heading-class-projectile-continued">Class ‘Projectile’ continued</h3>
<p>Okay, back to the Projectile class. The animation stays exactly the same, so you can use the animate() function from any of the other classes. In the update function however, we need to write some code :</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">update</span>(<span class="hljs-params">self</span>):</span> <span class="hljs-comment">#THIS IS THE PROJECTILE CLASS' UPDATE FUNCTION NOT PLAYER</span>
        self.animate()
        self.rect.x += self.direction.x * self.speed
        self.rect.y += (self.direction.y * self.speed)
        self.lifetime -= <span class="hljs-number">1</span>
        <span class="hljs-keyword">if</span> self.lifetime &lt;= <span class="hljs-number">0</span>:
            self.kill()
        <span class="hljs-keyword">if</span> hasattr(self.player, <span class="hljs-string">'shield'</span>) <span class="hljs-keyword">and</span> self.player.shield != <span class="hljs-literal">None</span>: <span class="hljs-comment">#can't attack when shield is active         </span>
            <span class="hljs-keyword">if</span> self.mask.overlap(self.player.shield.mask, (self.player.shield.rect.x - self.rect.x, self.player.shield.rect.y - self.rect.y)):
                self.kill()                         <span class="hljs-comment">#offset is how far the masks are away from each other </span>
        <span class="hljs-keyword">if</span> self.mask.overlap(self.player.mask, (self.player.rect.x - self.rect.x, self.player.rect.y - self.rect.y)):
            <span class="hljs-keyword">if</span> self.player.target == self.enemy_that_shot_projectile <span class="hljs-keyword">and</span> self.player.is_attacking:
                self.kill() <span class="hljs-comment">#shot disappears if it hits the player</span>
            <span class="hljs-keyword">else</span>:
                self.player.health -= enemy_data[self.enemy_type][<span class="hljs-string">'damage'</span>] <span class="hljs-comment">#player only takes damage if he doesnt hit the projectile coming from the target enemy</span>
                self.kill() <span class="hljs-comment">#shot disappears if it hits the player</span>
</code></pre>
<p>The direction the projectile moves in is the same as the enemy’s direction vector, so we just pass it in when creating the object. Each time the projectile updates, we decrease 1 from its lifetime, so after about 30 seconds, the projectile should disappear. Now, disappearing doesn’t mean that it doesn’t have any effect, it just means that like a real projectile, it stops once it covers a certain range. If the player gets hit, we decrease its health by the amount of damage caused by the type of enemy that shot the projectile, as defined in settings.py. But if the player attacks the projectile, we simply kill the projectile using <code>self.kill()</code>.</p>
<p>Lets import this class into our Enemy file and create a function to call it :</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">shoot_projectile</span>(<span class="hljs-params">self</span>):</span>
        direction = math.Vector2(self.player.rect.x, self.player.rect.y) - math.Vector2(self.rect.x, self.rect.y) 
        <span class="hljs-keyword">if</span> direction.length() != <span class="hljs-number">0</span>:
            direction = direction.normalize()
        <span class="hljs-keyword">else</span>:
            direction = math.Vector2(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>)

        projectile = Projectile(self.rect.center, direction, <span class="hljs-number">5</span>, self.projectile_group, self.enemy_type, self.player, self)
        <span class="hljs-keyword">return</span> projectile.cooldown
</code></pre>
<p>Again, this is pretty standard logic. We figure out the direction vector (the difference of the player and enemy vector) and pass it into the projectile object. We then return the cooldown which will be our enemy’s attack timer. Now we can call this function in the enemy’s update method to make it attack.</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">update</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">if</span> self.health &lt;= <span class="hljs-number">0</span>:
            self.player.points += <span class="hljs-number">2</span> <span class="hljs-comment">#increase 2 points only when the enemy is actually dead</span>
            self.kill()

        player_vector = math.Vector2(self.player.rect.center)
        enemy_vector = math.Vector2(self.rect.center)
        distance = player_vector.distance_to(enemy_vector)
        direction = (player_vector - enemy_vector)

        <span class="hljs-keyword">if</span> self.player.rect.x &gt; self.rect.x :
            dir = <span class="hljs-string">"right"</span>
        <span class="hljs-keyword">else</span>:
            dir = <span class="hljs-string">"left"</span>

        <span class="hljs-keyword">if</span> distance &lt;= self.notice_r <span class="hljs-keyword">and</span> distance &gt;self.attack_r:
            self.move(direction) 
        <span class="hljs-keyword">if</span> distance &lt;= self.attack_r <span class="hljs-keyword">and</span> self.attack_timer&lt;=<span class="hljs-number">0</span>:
            self.attack_timer = self.shoot_projectile()

        self.animate(dir)
        <span class="hljs-keyword">if</span> self.attack_timer &gt; <span class="hljs-number">0</span>:
            self.attack_timer -= <span class="hljs-number">1</span>

        <span class="hljs-keyword">if</span> hasattr(self.player, <span class="hljs-string">'shield'</span>) <span class="hljs-keyword">and</span> self.player.shield:
            shield_center = self.player.shield.rect.center
            enemy_center = self.rect.center

            dx = enemy_center[<span class="hljs-number">0</span>] - shield_center[<span class="hljs-number">0</span>] <span class="hljs-comment"># since its enemy - shield it'll create a vector pointing from the SHIELD to the ENEMY (opp dir)</span>
                                                    <span class="hljs-comment"># if enemy is to the left, dx = -ve, which'll make it move more to the left, if enemy is to the right, dx = +ve, which'll make it move more to the right</span>
            dy = enemy_center[<span class="hljs-number">1</span>] - shield_center[<span class="hljs-number">1</span>]
            distance = hypot(dx, dy)

            <span class="hljs-keyword">if</span> distance &lt; <span class="hljs-number">100</span>:
                <span class="hljs-comment"># moves the enemy away from the shield along the vector pointing from the shield to the enemy</span>
                direction = math.Vector2(dx, dy)
                <span class="hljs-keyword">if</span> direction.length() &gt; <span class="hljs-number">0</span>:
                    direction = direction.normalize()
                    self.rect.x += direction.x * self.speed
                    self.rect.y += direction.y * self.speed
                <span class="hljs-keyword">return</span>  <span class="hljs-comment"># stops normal movement like the projectile</span>
</code></pre>
<p>To sum up, we check the coordinates of the player and the enemy: if the player is to the right of the enemy, the enemy faces right and vice versa. Then, if the player is within the enemy’s notice radius, we call the <code>move()</code> function to make it move closer, and once its close enough (within the attack radius), we call the <code>shoot_projectile()</code> function and set the attack timer to the projectile’s cooldown (which we returned, remember?). One special edition here is that I wrote a couple lines of code to actively move the enemy away from the player (in a vector pointing opposite to the direction of the player) if the player’s shield has been activated. This gives the effect of a protective halo around the player, because even though we block the projectile attacks in our code, its good to provide visual confirmation too.</p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>That’s a wrap for part 3! Now that we’ve made our enemies, the only thing really left to do is to refine our code and add some UI/UX elements, which we’ll cover in the next part. We <em>MIGHT also</em> implement a pathfinding algorithm for the enemy or make them smarter using AI, but the game should work perfectly fine without it. Anyway, thank you for making it to the end and see you next time :)</p>
]]></content:encoded></item><item><title><![CDATA[Building a fantasy top-down game in Python (Part 2)]]></title><description><![CDATA[Now that we have our map and project setup, we can move on to coding our main character. Since this is a Geronimo Stilton inspired game, I chose a mouse as the sprite (unique, I know). I found this sprite for $2 on itch.io, I’ll link it below, but yo...]]></description><link>https://webwidewit.com/building-a-fantasy-top-down-game-in-python-part-2</link><guid isPermaLink="true">https://webwidewit.com/building-a-fantasy-top-down-game-in-python-part-2</guid><category><![CDATA[pygame]]></category><category><![CDATA[Python]]></category><category><![CDATA[coding]]></category><category><![CDATA[Programming Blogs]]></category><category><![CDATA[Game Development]]></category><dc:creator><![CDATA[Fatima Ali]]></dc:creator><pubDate>Tue, 17 Jun 2025 18:30:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1755538053560/27d56775-d2cd-4e35-933d-eae5fd6dec5f.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Now that we have our map and project setup, we can move on to coding our main character. Since this is a Geronimo Stilton inspired game, I chose a mouse as the sprite (<em>unique, I know</em>). I found this sprite for $2 on itch.io, I’ll link it below, but you can use - or better, make - your own sprite sheet. Before we begin figuring out how to make the player, lets start by outlining what exactly we want it to do :</p>
<ul>
<li><p>Properties : The player will have attributes like number of lives, health, coins and points, as well as the magic objects it has collected so far. Physically, it will be represented by an image, or rather a sequence of images called a sprite sheet which we will discuss later on.</p>
</li>
<li><p>Movement : The player can move in all 4 directions, but not on top of objects in our ‘Boundaries’ layer, for example trees and rocks. The arrow keys will trigger these movements.</p>
</li>
<li><p>Attack : The player will be able to attack the enemies when we press the ‘a’ key.</p>
</li>
<li><p>Spells : The shield spell will automatically enable once the player as gathered 20 coins. Once it is activated, it will block out all enemies within the player’s radius and last for 8 seconds before the coin count resets. The other spell is the reveal spell, which can be cast by the player by pressing the ‘r’ key. It will last for 12 seconds, and will recharge after 24 seconds. The reveal spell, as the name suggests, will reveal the 5 hidden magic objects to the player.</p>
</li>
<li><p>Collision : Collision detection will depend on the type of object the player collides with. For example, if the player collides with a gem, it will collect it and the coin count will increase. Collisions with a hazard will decrease the player’s lifespan, but there are no health-related repercussions to colliding with a boundary, it’ll just have to go around it.</p>
</li>
</ul>
<h3 id="heading-sprite-animations">Sprite animations</h3>
<p>Let’s start with the easiest animation first, the hazards. I only have one type of hazard in my game, but you can use the same code for multiple. I don’t have a sprite sheet for hazards, but rather 4 consecutive images in the same folder that I’ll loop over to create animation. Let’s start adding the animation function to our Hazard class now :</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">animate</span>(<span class="hljs-params">self</span>):</span>
        self.current_sprite += <span class="hljs-number">0.15</span>
        <span class="hljs-keyword">if</span> self.current_sprite &gt;= <span class="hljs-number">3</span>:
            self.current_sprite = <span class="hljs-number">0</span>
        self.image = image.load(<span class="hljs-string">f'levels//level_1//tilesets//hazards//plant_<span class="hljs-subst">{int(self.current_sprite)}</span>.png'</span>) <span class="hljs-comment">#simple sprite animation. this is why i made a class for hazards and didnt just use tile</span>
        self.mask = mask.from_surface(self.image)
</code></pre>
<p>This function uses the class variable <code>current_sprite</code> to keep track of the animation frame we are currently on. Each time our game updates, which is at 60 frames per second, our <code>current_sprite</code> increases by 0.15. Now the reason we increase it by that and not 1 is so that the animation isn’t too fast. Once all the images in the sequence have loaded, we reset <code>current_sprite</code> to 0 to repeat the animation. My hazard images are named ‘plant 0’ … ‘plant 3’ so I set <code>self.image</code> to the directory they’re in, changing the image number according to <code>current_sprite</code> each time.</p>
<p>Moving on to animating some actual sprite sheets, lets animate the player. We’ve already decided on what the player can do, the animation just has to reflect that. To recap, the player can move or remain idle in all 4 directions + it can attack.</p>
<p>Let’s start by writing a function to load the sprite sheet :</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">load_sprite_sheet</span>(<span class="hljs-params">self, path, frame_width, frame_height</span>):</span>
        sheet = image.load(path).convert_alpha()
        sw, sh = sheet.get_size()
        <span class="hljs-comment">#Note: the image you're using should have proper sprites of the size frame_width * frame_height</span>
        <span class="hljs-comment">#at proper space increments.</span>
        sprites = []
        <span class="hljs-keyword">for</span> y <span class="hljs-keyword">in</span> range(<span class="hljs-number">0</span>, sh, frame_height):
            <span class="hljs-keyword">for</span> x <span class="hljs-keyword">in</span> range(<span class="hljs-number">0</span>, sw, frame_width):
                frame = Surface((frame_width, frame_height), SRCALPHA)
                frame.blit(sheet, (<span class="hljs-number">0</span>, <span class="hljs-number">0</span>), (x, y, frame_width, frame_height)) <span class="hljs-comment">#blit different parts of the spritesheet </span>
                sprites.append(frame)

        <span class="hljs-keyword">return</span> sprites
</code></pre>
<p>This function basically returns a list containing images from a given sprite sheet, by cropping parts of it onto different transparent surfaces of <code>frame_width</code> and <code>frame_height</code>. Since our sprite is 64×64 px, these 2 variables will almost always be those dimensions but I haven’t hardcoded it to be more flexible.</p>
<p>We will then use this function to load our movement (which I call running in my code) and attack spreadsheets in the function below :</p>
<pre><code class="lang-python"> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">load_images</span>(<span class="hljs-params">self</span>):</span>
        states = {}
        run_right = self.load_sprite_sheet(<span class="hljs-string">'assets//sprites//level_1_mc//running.png'</span>, <span class="hljs-number">64</span>, <span class="hljs-number">64</span>)
        run_left = [transform.flip(img, <span class="hljs-literal">True</span>, <span class="hljs-literal">False</span>) <span class="hljs-keyword">for</span> img <span class="hljs-keyword">in</span> run_right]
        run_up = [transform.rotate(img, <span class="hljs-number">90</span>) <span class="hljs-keyword">for</span> img <span class="hljs-keyword">in</span> run_right]
        run_down = [transform.rotate(img, <span class="hljs-number">-90</span>) <span class="hljs-keyword">for</span> img <span class="hljs-keyword">in</span> run_right]
        <span class="hljs-comment">#...</span>
</code></pre>
<p>‘Running.png’ is a sprite sheet of the player running in the RIGHT direction. Notice that we can get a sprite sheet of the player running left just by flipping the images horizontally? Well that’s what we’re doing in line 4! Since <code>run_right</code> contains a list of consecutive images of the player running right, we use array comprehension to create a new array <code>run_left</code> which stores image in <code>run_right</code> after flipping it horizontally. The issue at hand here, is that in the asset pack I used, there were no sprite sheets for upward and downward movement (front and back profiles of the MC). So I came up with a very wonky solution, which was to just rotate the player 90 degrees to make it look like it was going up, and -90 degrees to make it run down.</p>
<p>The last thing left to do in this function is to add all these lists to our dictionary <code>states</code>. To make directions easier to set, I represent them with their initial, as in <code>r</code> for right, <code>l</code> for left etc. We can then define states for each of the directions as <code>states[‘running_r’] = run_right</code> and changing the key and value for each direction. Once you have movement down, you can do the exact same for attack in all direction.</p>
<p>Now, before we get to writing our animate() function, we need to know <em>when</em> to animate the player. Let’s write a function to get input from the user to determine this :</p>
<pre><code class="lang-python">    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">input</span>(<span class="hljs-params">self</span>):</span>
        self.animate = <span class="hljs-literal">False</span>
        keys = key.get_pressed()
        <span class="hljs-keyword">if</span> keys[K_RIGHT]: 
            self.direction.x = <span class="hljs-number">1</span>
            self.current_state = <span class="hljs-string">"r"</span>
            self.animate = <span class="hljs-literal">True</span>

        <span class="hljs-keyword">elif</span> keys[K_LEFT]:
            self.direction.x = <span class="hljs-number">-1</span> 
            self.current_state = <span class="hljs-string">"l"</span>
            self.animate = <span class="hljs-literal">True</span>
        <span class="hljs-keyword">else</span>:
            self.direction.x = <span class="hljs-number">0</span>

        <span class="hljs-keyword">if</span> keys[K_UP]:
            self.direction.y = <span class="hljs-number">-1</span> 
            self.current_state = <span class="hljs-string">"u"</span>
            self.animate = <span class="hljs-literal">True</span>
        <span class="hljs-keyword">elif</span> keys[K_DOWN]:
            self.direction.y = <span class="hljs-number">1</span> 
            self.current_state = <span class="hljs-string">"d"</span>
            self.animate = <span class="hljs-literal">True</span>
        <span class="hljs-keyword">else</span>:
            self.direction.y = <span class="hljs-number">0</span>

        <span class="hljs-keyword">if</span> keys[K_a]:
            self.is_attacking = <span class="hljs-literal">True</span>
            self.weapon = <span class="hljs-string">"attack"</span>
            self.attack_time = time.get_ticks()

        <span class="hljs-keyword">if</span> keys[K_c] <span class="hljs-keyword">and</span> <span class="hljs-string">'special'</span> <span class="hljs-keyword">in</span> self.spells: <span class="hljs-comment">#unlock special weapon if the player has collected at least 30 coins. only this special weapon can kill the ending fairies</span>
            self.is_attacking = <span class="hljs-literal">True</span>
            self.weapon = <span class="hljs-string">"special"</span>
            self.attack_time = time.get_ticks()
</code></pre>
<p>This is a pretty simple function, it detects which key has been pressed and sets the <code>current_direction</code> to it. If we press the ‘a’ key, <code>is_attacking</code> becomes True . We can now animate using all these functions.</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">animate_player</span>(<span class="hljs-params">self</span>):</span>
        direction = self.current_state

        <span class="hljs-keyword">if</span> self.animate:
            <span class="hljs-keyword">if</span> self.is_attacking:
                animation = self.sprite[self.weapon+<span class="hljs-string">"_"</span> + direction]
                self.current_sprite += <span class="hljs-number">0.15</span>
                <span class="hljs-keyword">if</span> self.current_sprite &gt;= len(animation):
                    self.current_sprite = <span class="hljs-number">0</span>
                self.image = animation[int(self.current_sprite)]
            <span class="hljs-keyword">else</span>:
                animation = self.sprite[<span class="hljs-string">'running_'</span> + direction]
                self.current_sprite += <span class="hljs-number">0.15</span>
                <span class="hljs-keyword">if</span> self.current_sprite &gt;= len(animation):
                    self.current_sprite = <span class="hljs-number">0</span>
                self.image = animation[int(self.current_sprite)]
        <span class="hljs-keyword">else</span>:
            <span class="hljs-keyword">if</span> self.is_attacking:
                animation = self.sprite[self.weapon+<span class="hljs-string">"_"</span> + direction]
                self.current_sprite += <span class="hljs-number">0.1</span>
                <span class="hljs-keyword">if</span> self.current_sprite &gt;= len(animation):
                    self.current_sprite = <span class="hljs-number">0</span>
                self.image = animation[int(self.current_sprite)]
            <span class="hljs-keyword">else</span>:
                self.image = self.sprite[<span class="hljs-string">'idle_'</span> + direction]

        self.mask = mask.from_surface(self.image)
        self.rect = self.image.get_rect(center=self.rect.center)
</code></pre>
<p>This is a variation on our initial animate function that we used for <code>hazards</code>. Before you do this, you’ll need to call <code>load_images()</code> in your constructor and store the dictionary <code>states</code> in a class variable. I call this variable <code>sprite</code>.</p>
<p>Remember how we created states? We set an action + ‘_’ + r, l, u or d as the key and the corresponding list of images as the value. Well, that dictionary is precisely what we use now to load the correct set of images and run the animation.</p>
<p>For enemies, use the same code to load the sprite sheets and then the animation function from hazards (since enemies don’t require user input to determine their direction like the player). Later, when we code our enemy class, we’ll modify the function slightly to make sure the enemies always face the player.</p>
<h3 id="heading-the-player">The player</h3>
<p>To construct the player class, we’ll start by translating some of the things we wrote above, about what we wanted the player to do, into code. Beginning with the constructor :</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Player</span>(<span class="hljs-params">sprite.Sprite</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, pos, groups, obstacles</span>):</span>
        super().__init__(groups)
        self.sprite = self.load_images()
        self.current_sprite = <span class="hljs-number">0.0</span>
        self.animate = <span class="hljs-literal">False</span>
        self.current_state = <span class="hljs-string">"r"</span>
        self.image = self.sprite[<span class="hljs-string">"idle_"</span>+self.current_state]
        self.rect = self.image.get_rect(topleft = pos)
        self.direction = math.Vector2() <span class="hljs-comment">#x and y position of player as a vector. keyboard input will change these values</span>
        self.obstacles = obstacles
        self.mask = mask.from_surface(self.image)
        self.coins = <span class="hljs-number">0</span>
        self.points = <span class="hljs-number">0</span>
        self.is_attacking = <span class="hljs-literal">False</span>
        self.attack_time = <span class="hljs-literal">None</span>
        self.weapon = <span class="hljs-string">"attack"</span>
        self.current_spell = <span class="hljs-literal">None</span>
        <span class="hljs-comment">#UI healthbar</span>
        self.condition = {<span class="hljs-string">'health'</span> : <span class="hljs-number">100</span>, <span class="hljs-string">'attack'</span> : <span class="hljs-number">10</span>, <span class="hljs-string">'spells'</span> : <span class="hljs-number">1</span>, <span class="hljs-string">'weapons'</span> : <span class="hljs-number">1</span>, <span class="hljs-string">'speed'</span> : <span class="hljs-number">7</span>, <span class="hljs-string">'lives'</span> : <span class="hljs-number">3</span>}
        self.health = self.condition[<span class="hljs-string">'health'</span>]
        self.spells = [<span class="hljs-string">'regular'</span>]
        self.speed = self.condition[<span class="hljs-string">'speed'</span>]
        self.lives = self.condition[<span class="hljs-string">'lives'</span>]
        self.magic_objs = []
        self.reveal_timer = <span class="hljs-number">0</span>
</code></pre>
<p>So… that’s a <em>lot</em> of variables, but we’ll use them all some way or the other I promise. Moving on to collision detection :</p>
<pre><code class="lang-python">      <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">collision</span>(<span class="hljs-params">self,dir, old_rect</span>):</span>  
        <span class="hljs-keyword">if</span> dir == <span class="hljs-string">'horizontal'</span>:
            <span class="hljs-keyword">for</span> sprite <span class="hljs-keyword">in</span> self.obstacles:
                <span class="hljs-keyword">if</span> self.mask.overlap(sprite.mask, (sprite.rect.x - self.rect.x, sprite.rect.y - self.rect.y)):
                    self.rect.x = old_rect.x <span class="hljs-comment">#undo the move</span>
                    <span class="hljs-keyword">if</span> sprite.sprite_type == <span class="hljs-string">'collectible'</span>:
                        sprite.kill() 
                        self.coins += <span class="hljs-number">1</span>
                        self.points += <span class="hljs-number">1</span>
                    <span class="hljs-keyword">if</span> sprite.sprite_type == <span class="hljs-string">'magic_obj'</span>:
                        sprite.power_up(self)
                        sprite.kill() <span class="hljs-comment"># i realized that if i killed the obj to remove it from the screen, that would mean removing it from all groups, which would mean that it didnt get updated so the cooldown wouldnt work</span>
                                        <span class="hljs-comment">#instead i add it to a group call collected and update that group in my level class</span>
                        self.collected.add(sprite)
                        self.magic_objs.append(sprite.magic_type)
                        self.points += <span class="hljs-number">5</span>
                    <span class="hljs-keyword">if</span> sprite.sprite_type == <span class="hljs-string">"hazard"</span> <span class="hljs-keyword">and</span> self.current_spell!=<span class="hljs-string">'shield_circle'</span>:
                        time.wait(<span class="hljs-number">400</span>)
                        self.health -= <span class="hljs-number">30</span>
</code></pre>
<p>collision() takes in 2 parameters, <code>dir</code> , which can be either horizontal or vertical and <code>old_rect</code>, which stores the old position of the player so that we can undo a move. First, we check for horizontal collisions, then vertical so we know whether to undo the move in the x direction or the y. We start with looping over all the obstacles, and checking for collisions with each one of them. Let me break down the line that actually does the check :</p>
<pre><code class="lang-python"><span class="hljs-keyword">if</span> self.mask.overlap(sprite.mask, (sprite.rect.x - self.rect.x, sprite.rect.y - self.rect.y))
</code></pre>
<p>So what I’m doing here, is basically checking if there is any overlap between the masks of the obstacle sprite and the player sprite. Remember from part 1 how I created a variable <code>self.mask</code> in the class template for Tile, which we used as a base for all classes? Well what that mask is, is a precise pixel by pixel outline of the image we use as a sprite. Mask collisions are much more precise than the usual <code>rect</code> collisions which you may have seen, thus to make things smoother, I’m using those here.</p>
<p>The rest is pretty straightforward, if we collide with an obstacle of the type ‘Collectible’, we remove it from the screen (to show that it’s been collected of course), by ‘killing’ it. Then we increase our coin count and points by one. Collisions with magic objects and hazards do exactly what the code implies.<br />Also, ignore this part in the third to last line for now : <code>self.current_spell!='shield_circle'</code></p>
<p>You can now copy-paste this block and make <code>dir == ‘vertical’</code> instead. Okay so now we have - collisions, animations and input down. Let’s tackle movement next.</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">move</span>(<span class="hljs-params">self</span>):</span>
        old_rect = self.rect.copy() <span class="hljs-comment">#store the prev positions incase i need to undo a move in collision</span>
        <span class="hljs-comment">#When we press both up/down and right/left keys together, the player moves significantly faster. </span>
        <span class="hljs-comment">#Here, I check if the vector has a magnitude, which is possible if there is movement in both the axis. </span>
        <span class="hljs-comment">#If there is, I normalize the vector to have a magnitude of 1 so that speed in that direction stays the same as the speed moving only horizontally or vertically</span>
        <span class="hljs-keyword">if</span> self.direction.magnitude() != <span class="hljs-number">0</span>: 
            self.direction = self.direction.normalize()
        self.rect.x += self.direction.x * self.speed
        self.collision(<span class="hljs-string">"horizontal"</span>, old_rect) <span class="hljs-comment">#check for horizontal collisions when moving on the x axis so we dont accidently reverse the y move</span>
        self.rect.y += self.direction.y * self.speed
        self.collision(<span class="hljs-string">"vertical"</span>, old_rect) <span class="hljs-comment">#check for vertical collisions when moving on the y axis so we dont accidently reverse the x move</span>
</code></pre>
<p>Remember to call both <code>move()</code>, <code>animate_player()</code> and <code>input()</code> in your <code>update()</code> method.</p>
<p>We can now work on our spell function.</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">cast_spell</span>(<span class="hljs-params">self</span>):</span>
        keys = key.get_pressed()
        <span class="hljs-keyword">if</span> keys[K_r] <span class="hljs-keyword">and</span> self.current_spell == <span class="hljs-literal">None</span> <span class="hljs-keyword">and</span> self.can_cast_again:
            self.current_spell = <span class="hljs-string">"reveal"</span>
            self.cast_time = time.get_ticks()
         <span class="hljs-comment">#What this segment does, is adds the hidden magic objects </span>
         <span class="hljs-comment">#to self.visible, which makes them show up on screen.</span>
            <span class="hljs-keyword">if</span> hasattr(self, <span class="hljs-string">'hidden'</span>):
                <span class="hljs-keyword">for</span> spr <span class="hljs-keyword">in</span> self.hidden:
                    self.groups()[<span class="hljs-number">0</span>].add(spr)
                    self.obstacles.add(spr)
            self.dark_overlay = pygame.Surface(display.get_surface().get_size(), pygame.SRCALPHA)

        <span class="hljs-keyword">if</span> self.coins &gt;= <span class="hljs-number">25</span> <span class="hljs-keyword">and</span> self.current_spell == <span class="hljs-literal">None</span>:
            self.cast_time = time.get_ticks()
            self.current_spell = <span class="hljs-string">'shield_circle'</span>
            shield_img = transform.scale(image.load(<span class="hljs-string">'assets//sprites//level_1_mc//shield_circle.png'</span>), (<span class="hljs-number">100</span>,<span class="hljs-number">100</span>))
            self.shield = Tile((self.rect.centerx, self.rect.centery), [self.groups()[<span class="hljs-number">0</span>], self.obstacles], <span class="hljs-string">'protection'</span>, shield_img) 

        <span class="hljs-comment">#self.groups()[0] is just visible sprites. i add it to visible sprites when its active and remove it by killing it when not</span>
</code></pre>
<p>This is the implementation of the spells part of the player we outlined at the beginning. We check if the player is currently under any spell, and if the cooldown for the last one has worn off (<code>self.can_cast_again</code>). Then, if the play has pressed the ‘r’ key, we activate the reveal spell and save the time in <code>self.cast_time</code> (so that we can calculate how much time has passed since we cast it, and de-activate it accordingly).</p>
<p>If the player has collected more than 25 coins, we activate the shield spell and draw a shield around the player.</p>
<p><code>self.dark_overlay</code> is a transparent-y black surface the size of the screen.<br />We can’t actually draw anything on screen through this function, but we can access the class variables and draw them in our Level class’ draw function, as such :</p>
<pre><code class="lang-python"><span class="hljs-keyword">if</span> hasattr(player, <span class="hljs-string">'dark_overlay'</span>) <span class="hljs-keyword">and</span> player.dark_overlay != <span class="hljs-literal">None</span>:
   player.dark_overlay.fill((<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">180</span>))
   draw.circle(player.dark_overlay, (<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>), player.rect.center- self.offset, <span class="hljs-number">64</span>) <span class="hljs-comment">#circling the player and the objects with a transparent circle to give a flashlight effect</span>
   <span class="hljs-keyword">for</span> obj <span class="hljs-keyword">in</span> player.hidden:
       draw.circle(player.dark_overlay, (<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>), obj.rect.center - self.offset, <span class="hljs-number">64</span>) 
   self.screen.blit(player.dark_overlay, (<span class="hljs-number">0</span>, <span class="hljs-number">0</span>))
</code></pre>
<p>Finally, <em>for now</em>, we can add some stuff to our player’s update function to deal with spell de-activation.</p>
<pre><code class="lang-python">        <span class="hljs-keyword">if</span> self.health &lt;= <span class="hljs-number">0</span>:
            self.lives -= <span class="hljs-number">1</span>
            self.health = <span class="hljs-number">100</span>

        self.cast_spell()

        <span class="hljs-keyword">if</span> self.current_spell == <span class="hljs-string">'shield_circle'</span> <span class="hljs-keyword">and</span> hasattr(self, <span class="hljs-string">'shield'</span>):
            self.shield.rect.center = self.rect.center

        <span class="hljs-keyword">if</span> self.current_spell:
            <span class="hljs-keyword">if</span> time.get_ticks() - self.cast_time &gt;= protection[current_level][self.current_spell][<span class="hljs-string">'lasts_for'</span>]:
                <span class="hljs-keyword">if</span> self.current_spell == <span class="hljs-string">'shield_circle'</span>: <span class="hljs-comment">#once the shield circle is over i kill the shield</span>
                    self.shield.kill() <span class="hljs-comment">#remove the shield from the screen.</span>
                <span class="hljs-keyword">elif</span> self.current_spell == <span class="hljs-string">'reveal'</span>:
                    self.cast_time = time.get_ticks()
                    self.can_cast_again = <span class="hljs-literal">False</span>
                    <span class="hljs-keyword">for</span> spr <span class="hljs-keyword">in</span> self.hidden:
                        self.groups()[<span class="hljs-number">0</span>].remove(spr) <span class="hljs-comment"># remove trhe objects from sight</span>
                        self.obstacles.remove(spr)
                    self.dark_overlay = <span class="hljs-literal">None</span>

                self.current_spell = <span class="hljs-literal">None</span>
                self.coins = <span class="hljs-number">0</span> <span class="hljs-comment">#resets the coin count each time so i can activate the shield again and again when the criteria is met</span>
                print(<span class="hljs-string">'over'</span>) <span class="hljs-comment">#just print debugging </span>


        <span class="hljs-keyword">if</span> time.get_ticks() - self.cast_time &gt;= protection[current_level][<span class="hljs-string">'reveal'</span>][<span class="hljs-string">'cooldown'</span>]:
            self.can_cast_again = <span class="hljs-literal">True</span> <span class="hljs-comment">#after 24 seconds</span>
</code></pre>
<h3 id="heading-conclusion">Conclusion</h3>
<p>And that’s it for part 2! There is a lot you can customize here, from spells to movement and even attack logic, so don’t be afraid to tweak and play around :)</p>
<p>Main character sprite sheet : <a target="_blank" href="https://14collective.itch.io/fantasy-mice">https://14collective.itch.io/fantasy-mice</a></p>
]]></content:encoded></item><item><title><![CDATA[Building a fantasy top-down game in Python (Part 1)]]></title><description><![CDATA[I was required to submit a real-world python project for my grade 11 Computer Science class this year, and what better project to build than a game! Not only are games fun, but they help visualize exactly what complex pieces of code do, for example c...]]></description><link>https://webwidewit.com/building-a-fantasy-top-down-game-in-python-part-1</link><guid isPermaLink="true">https://webwidewit.com/building-a-fantasy-top-down-game-in-python-part-1</guid><category><![CDATA[Python]]></category><category><![CDATA[Game Development]]></category><category><![CDATA[pygame]]></category><category><![CDATA[General Programming]]></category><dc:creator><![CDATA[Fatima Ali]]></dc:creator><pubDate>Mon, 09 Jun 2025 18:30:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1755537996974/c8be8f7d-5743-4bb3-9b50-6c35969266e7.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I was required to submit a real-world python project for my grade 11 Computer Science class this year, and what better project to build than a game! Not only are games fun, but they help visualize exactly what complex pieces of code do, for example classes and objects. The game I originally wanted to make was a Geronimo Stilton inspired fantasy top-down with 5 levels corresponding to 5 of the lands in ‘Geronimo Stilton and the Kingdom of Fantasy’, like the land of sweets, the land of fairies etc. But, I soon came to realize that I was in way over my head cause it took me the entirety of the allotted time just to build Level 1, which in this series, we will learn to build together!</p>
<h3 id="heading-designing-the-map">Designing the map</h3>
<p>Okay, so before we even get started with the code, we’ll need to design the map of the level. Now if this was a simpler level, we could’ve gotten away with creating a 2D array to store map information, but since we have multiple layers (which I will soon discuss), we’ll need something more large-scale. For that purpose, we’ll be using Tiled as our editor!</p>
<p>To get started, download the latest version of Tiled (<a target="_blank" href="http://www.mapeditor.org/download.html">http://www.mapeditor.org/download.html</a>) on your device and follow the installation guide. Once you have it installed, go to ‘New’ and select ‘New Map’, then create a 40×30 map with 64×64px tiles. These are the dimensions I used for mine, but you can adjust them to make yours larger or smaller. Now, we can get to the layers.</p>
<p>Layers in a map are used to group elements of the same type, or rather elements that serve the same purpose, together. In our game, we’ll have 7 layers :</p>
<ul>
<li><p>Background : As the name suggests, the background contains paths and greenery etc., basically anything the player can walk on. You can use an image for this or design it manually by placing tiles.</p>
</li>
<li><p>Boundaries : This layer will contain the tiles lining the edge of the map to prevent the player going out of the screen. Also, it will have the solid objects like trees, rocks etc. , that the player cannot walk on.</p>
</li>
<li><p>Collectibles : This layer will contain gems that the player can pick up along the way.</p>
</li>
<li><p>Enemies : We’ll position the enemies throughout the map and put the attack logic in our code.</p>
</li>
<li><p>Hazards : Traps or obstacles that can harm the player.</p>
</li>
<li><p>Magic Objects : This layer will store the position of the 5 magic objects the player will have to collect in the game.</p>
</li>
<li><p>Portal : The portal is the gateway to crystal castle (the game destination). This layer will only contain 1 element and that is the portal placed at the end of the map.</p>
</li>
</ul>
<p>The reason I’ve separated these elements into layers is because each layer will be loaded as objects of its corresponding class in my code later on. For example, all gems will be objects of the <code>Collectible</code> class. Remember, these layers should be TILE layers.</p>
<p>This is what your background should somewhat look like at this point. On top of this, we’ll then place our objects in their corresponding layers.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1754377131271/a5c0580e-4cb3-492c-839d-ed33d225c845.png" alt class="image--center mx-auto" /></p>
<p>To add boundaries, hazards, collectibles etc., you will first have to import their images as a tile set. You can do this by heading over to file and creating a new tile set, selecting the image of your choice as the source. Once you have these tile sets ready, you can start placing them all over the map.</p>
<p>Now the only thing left to do is to export this map so we can use it in our code. To do this, we’ll export the background as an image separately and the rest of the layers together as a <code>.tmj</code> file.</p>
<h3 id="heading-project-setup">Project setup</h3>
<p>To start setting up the project, create two folders, one to store level information and the other for assets, such as the sprite sheets. The directory should look something like this :</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1754400178173/29cac92b-2ffb-49e3-b351-c12489c6b6a3.png" alt class="image--center mx-auto" /></p>
<p>In this part we’ll deal only with <code>level.py</code>, <code>map_loader.py</code> and <code>main.py</code> so you can ignore the rest for now. But first, remember to import all the image files you used to create your tile sets in Tiled into your assets or levels folder. My tile sets are stored in levels (this will be important later on). The main file is fairly straightforward, we import the libraries we need along with the <code>Level</code> class from our level.py file, and initialize the game loop.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> sys, pygame
<span class="hljs-keyword">from</span> pygame <span class="hljs-keyword">import</span> *
<span class="hljs-keyword">from</span> level <span class="hljs-keyword">import</span> Level

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Game</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self</span>):</span>
        pygame.init()
        self.screen = pygame.display.set_mode((<span class="hljs-number">1000</span>, <span class="hljs-number">500</span>))
        self.clock = time.Clock()
        self.level = Level(self.screen)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">run</span>(<span class="hljs-params">self</span>):</span>
            <span class="hljs-keyword">while</span> <span class="hljs-literal">True</span>:
                <span class="hljs-keyword">for</span> evt <span class="hljs-keyword">in</span> event.get():
                    <span class="hljs-keyword">if</span> evt.type == QUIT:
                        quit()
                        sys.exit()
                self.level.run() <span class="hljs-comment">#we'll write this function later</span>
                pygame.display.update()
                self.clock.tick(<span class="hljs-number">60</span>)

<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">'__main__'</span>:
    game = Game()
    game.run()
</code></pre>
<p>Next, we can start writing our level class. This will initialize two sprite groups, visible sprites, and obstacle sprites. As the names suggest, visible sprites contains all the sprites that will get drawn on the screen whereas obstacle sprites are the sprites we will detect player collision with.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> pygame <span class="hljs-keyword">import</span> *
<span class="hljs-keyword">from</span> map_loader <span class="hljs-keyword">import</span> Map

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Level</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, screen</span>):</span>
        self.screen = screen
        <span class="hljs-comment">#visible sprites is the only group that I'll draw on the screen. Obstacles are sprites that can collide with the player</span>
        self.visible = Camera()
        self.obstacles = Camera()
        self.attackable = sprite.Group()
        self.hidden = sprite.Group()
        self.player = <span class="hljs-literal">None</span> <span class="hljs-comment">#we havent made the player yet</span>
        self.map = Map(self.visible, self.obstacles, self.player, self.attackable, self.hidden) <span class="hljs-comment">#we'll do this next</span>
        self.ui = UI()


    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">run</span>(<span class="hljs-params">self</span>):</span>
        self.visible.draw(self.player)
        self.visible.update()
        self.hidden.update() <span class="hljs-comment">#update groups</span>
        <span class="hljs-keyword">return</span>
</code></pre>
<p>You will notice that both <code>visible</code> and <code>obstacle</code> are not initialized as sprite groups, but as instances of the <code>Camera</code> class. This is because I wanted to create the illusion of a camera following the player, and to do that you need to move everything else around the player in a way that centers it. We can accomplish this by offsets.</p>
<p>In level.py, create another class called Camera that inherits from Pygame’s sprite group.</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Camera</span>(<span class="hljs-params">sprite.Group</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self</span>):</span>
        super().__init__() <span class="hljs-comment">#initialize the parent class of sprite.Group()</span>
        self.screen = display.get_surface()
        self.offset = math.Vector2()
        self.background = image.load(<span class="hljs-string">'levels\\level_1\\level_data\\background.png'</span>).convert()
        <span class="hljs-comment">#this is the background we exported as an image from tiled</span>
        self.bg_rect = self.background.get_rect(topleft = (<span class="hljs-number">0</span>,<span class="hljs-number">0</span>))

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">draw</span>(<span class="hljs-params">self, player</span>):</span> <span class="hljs-comment">#overwriting the inbuilt draw function</span>
        <span class="hljs-comment">#To create the illusion of a camera following the player, I add an offset to the position of the sprites surrounding the player based off the player's position</span>
        <span class="hljs-comment">#2528 = (64*40 =&gt; size of 1 tile x no. of horizontal tiles) - 64//2 (tilesize//2)</span>
        <span class="hljs-keyword">if</span> player.rect.x &lt; <span class="hljs-number">2528</span> - WIDTH//<span class="hljs-number">2</span>: <span class="hljs-comment">#right out of bounds</span>
            self.offset.x = player.rect.centerx - (self.screen.get_width()//<span class="hljs-number">2</span>) <span class="hljs-comment">#to center the player amidst the camera</span>
        <span class="hljs-comment">#1888 = (64*30 =&gt; size of 1 tile x no. of vertical tiles) - 64//2 (tilesize//2)</span>
        <span class="hljs-keyword">if</span> player.rect.y &lt; <span class="hljs-number">1888</span> - HEIGHT//<span class="hljs-number">2</span>: <span class="hljs-comment">#bottom out of bounds</span>
            self.offset.y = player.rect.centery - (self.screen.get_height()//<span class="hljs-number">2</span>)

        <span class="hljs-keyword">if</span> player.rect.x &lt; WIDTH//<span class="hljs-number">2</span>: <span class="hljs-comment">#left. if the player moves left past half of the current screen, there's no offset otherwise we get a bit of the black screen</span>
            self.offset.x = <span class="hljs-number">0</span>
        <span class="hljs-keyword">if</span> player.rect.y &lt; HEIGHT//<span class="hljs-number">2</span>: <span class="hljs-comment">#top</span>
            self.offset.y = <span class="hljs-number">0</span>
        <span class="hljs-comment">#create the background here so its always below the sprites</span>
        self.screen.blit(self.background, (self.bg_rect.topleft - self.offset))
        <span class="hljs-keyword">for</span> sprite <span class="hljs-keyword">in</span> self.sprites():
            offset_pos = sprite.rect.topleft - self.offset
            self.screen.blit(sprite.image, offset_pos)
</code></pre>
<p>Now, you have your player centered amidst the screen. However, you don’t have a player yet, so this won’t work. In order to see what we have built so far, you can use a placeholder image for <code>self.player</code>.</p>
<h3 id="heading-loading-the-map">Loading the map</h3>
<p>Finally, we get to the main part, or at least the one I struggled with <em>quite a bit</em>. But, before we get to it, you’ll need to initialize empty classes for enemy, player, hazard, magic object and portal, along with a general class ‘Tile’ which’ll act as the base for any tile on our map. We’ll use the following template for all these classes-</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> pygame <span class="hljs-keyword">import</span> *

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Tile</span>(<span class="hljs-params">sprite.Sprite</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, pos, groups, type, surface = surface.Surface(<span class="hljs-params">(<span class="hljs-params">TILESIZE, TILESIZE</span>), SRCALPHA</span>)</span>):</span>
        super().__init__(groups) <span class="hljs-comment">#adds the sprite to the given groups. </span>
        self.sprite_type = type  
        self.image = surface
        self.rect = self.image.get_rect(topleft = pos)
        self.mask = mask.from_surface(self.image) <span class="hljs-comment">#I'll explain this later on</span>
</code></pre>
<p>Now, in the main file, create a class called Map and initialize <code>self.visible</code>, <code>self.obstacles</code>, and <code>self.hidden</code> with the passed-in params. If you open up your map’s .tmj file, you’ll see that it contains JSON code, which we’ll need to read properly using the python JSON module.</p>
<p>Next, we’ll need to create 3 functions. The first will format the JSON code into a 2D array containing the locations (GIDs) of objects in each layer. The second, will create a tile set in the form of a dictionary, associating each tile’s GID (global index) to its corresponding tile image (our json file kinda does this already, but it associates each GID to its tile set file which is a .tmx file, we’ll clean this up so that it actually renders in pygame). The third will utilize these functions to create the actual map.</p>
<p>Let’s start with the first one.</p>
<pre><code class="lang-python"> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">format</span>(<span class="hljs-params">self,data</span>):</span>
        data_formatted = []
        c = <span class="hljs-number">0</span>
        <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(<span class="hljs-number">30</span>): <span class="hljs-comment">#30 rows in 1 column</span>
            row = data[c:<span class="hljs-number">40</span>+c] <span class="hljs-comment">#40 columns in 1 row</span>
            data_formatted.append(row)
            c += <span class="hljs-number">40</span>
        <span class="hljs-keyword">return</span> data_formatted
</code></pre>
<p>In our tmj file, the data for each layer isn’t stored as a 2d array of 30 rows and 40 columns, but instead as 1 massive 1D array of 1200 values. This function breaks it down into a 2D array with the same dimensions as our map.</p>
<p>Then, we need to write a function to create the tile set within python :</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">create_tileset</span>(<span class="hljs-params">self, data</span>):</span>

        <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">image_path</span>(<span class="hljs-params">path</span>):</span>
            <span class="hljs-keyword">return</span> <span class="hljs-string">f"levels\\level_1\\tilesets\\<span class="hljs-subst">{path.split(<span class="hljs-string">'/'</span>)[<span class="hljs-number">2</span>].split(<span class="hljs-string">'.'</span>)[<span class="hljs-number">0</span>]}</span>.png"</span>

        tileset = {} 
        <span class="hljs-keyword">for</span> tile <span class="hljs-keyword">in</span> data:
            tileset[tile[<span class="hljs-string">'firstgid'</span>]] = image_path(tile[<span class="hljs-string">'source'</span>])

        <span class="hljs-keyword">return</span> tileset
</code></pre>
<p>Okay I lied, its actually 1 <em>nested</em> function, but its pretty simple nonetheless. Let’s breakdown <code>create_tileset()</code> first. Our .tmj file contains an object literal (similar to a dictionary in python) where one of the keys is ‘tilesets’. It looks something like :</p>
<pre><code class="lang-json"><span class="hljs-string">"tilesets"</span>:[
        {
         <span class="hljs-attr">"firstgid"</span>:<span class="hljs-number">1</span>,
         <span class="hljs-attr">"source"</span>:<span class="hljs-string">"..\/tilesets\/forest.tsx"</span>
        },
        ... ]
</code></pre>
<p>Each tile set as you can see contains a ‘first GID’ and a ‘source’ which is the path to the tileset’s .tsx file in Tiled. Our loop creates a python dictionary setting this firstgid as a key and the source as its corresponding value after formatting it properly using <code>image_path()</code>, which creates a path to the tile’s image file by taking in the path to its .tsx file. In the tileset above, for example, image_path() will take in the source "..\/tilesets\/forest.tsx", remove the part after / and before .tsx which is forest, aka, the name of the tile, and position it between “levels\level_1\tilesets\ .png” which is where its image is stored. You can adjust this location according to where your tilesets are stored.</p>
<p>Finally, we get to the main function. You’ll need to initialize empty arrays for each of the layers, so we can extract information from our tiled file into them. Then, we’ll use the json module to actually load the file and read information from it, like so :</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">create_map</span>(<span class="hljs-params">self</span>):</span>
        magic =  [<span class="hljs-string">'key'</span>, <span class="hljs-string">'amulet'</span>, <span class="hljs-string">'potion'</span>, <span class="hljs-string">'cheese'</span>, <span class="hljs-string">'scroll'</span>] <span class="hljs-comment">#the 5 magical objects we placed around the map</span>
        shuffle(magic) <span class="hljs-comment">#randomize the positions everytime </span>
        boundary = []
        collectible = []
        hazard = []
        enemy = []
        portal = []
        magic_obj = []
                    <span class="hljs-comment">#path to your .tmj file</span>
        <span class="hljs-keyword">with</span> open(<span class="hljs-string">'levels\\level_1\\level_data\\level1_3.tmj'</span>, <span class="hljs-string">'r'</span>) <span class="hljs-keyword">as</span> file:
            data = json.load(file)
            self.tileset = self.create_tileset(data[<span class="hljs-string">'tilesets'</span>])

            <span class="hljs-keyword">for</span> layer <span class="hljs-keyword">in</span> data[<span class="hljs-string">'layers'</span>]:
                <span class="hljs-keyword">if</span> layer[<span class="hljs-string">'name'</span>] == <span class="hljs-string">"Boundaries"</span>:
                    boundary = layer[<span class="hljs-string">'data'</span>]
                <span class="hljs-keyword">if</span> layer[<span class="hljs-string">'name'</span>] == <span class="hljs-string">"collectible"</span>:
                    collectible = layer[<span class="hljs-string">'data'</span>]
                <span class="hljs-keyword">if</span> layer[<span class="hljs-string">'name'</span>] == <span class="hljs-string">'enemies'</span>:
                    enemy = layer[<span class="hljs-string">'data'</span>]
                <span class="hljs-keyword">if</span> layer[<span class="hljs-string">'name'</span>] == <span class="hljs-string">'hazards'</span>:
                    hazard = layer[<span class="hljs-string">'data'</span>]
                <span class="hljs-keyword">if</span> layer[<span class="hljs-string">'name'</span>] == <span class="hljs-string">'portal'</span>:
                    portal = layer[<span class="hljs-string">'data'</span>]
                <span class="hljs-keyword">if</span> layer[<span class="hljs-string">'name'</span>] == <span class="hljs-string">'magic_objects'</span>:
                    magic_obj = layer[<span class="hljs-string">'data'</span>]
        layers = {
                     <span class="hljs-string">'boundaries'</span> : self.format(boundary),
                     <span class="hljs-string">'collectibles'</span> :   self.format(collectible),
                     <span class="hljs-string">'enemies'</span> :   self.format(enemy),
                     <span class="hljs-string">'hazards'</span> :   self.format(hazard),
                     <span class="hljs-string">'portal'</span> :   self.format(portal),
                     <span class="hljs-string">'magic_objs'</span> :   self.format(magic_obj)
                    }
</code></pre>
<p>Make sure to match the names of the layers exactly to how they are in your json file, or you won’t be reading in any data. Now, we can loop over each layer in layers and create objects for each tile.</p>
<pre><code class="lang-python"><span class="hljs-comment">#the following loop iterates over all the layers inside my map, and creates an instance of the appropriate class based</span>
        <span class="hljs-comment">#on what the layer contains. for example, the portal is an instance of the Portal class. </span>
        <span class="hljs-comment">#based on whether or not i want them to show up on screen, i add them to the visible sprites group (self.visible) and all the obstacles</span>
        <span class="hljs-comment">#are in self.obstacles. </span>
        <span class="hljs-keyword">for</span> layer, map <span class="hljs-keyword">in</span> layers.items():
            <span class="hljs-keyword">for</span> rind, row <span class="hljs-keyword">in</span> enumerate(map):
                <span class="hljs-keyword">for</span> cind, col <span class="hljs-keyword">in</span> enumerate(row):
                    <span class="hljs-keyword">if</span> col != <span class="hljs-number">0</span>:
                        i = <span class="hljs-number">0</span>
                        x = cind * TILESIZE
                        y = rind * TILESIZE
                        <span class="hljs-keyword">if</span> layer == <span class="hljs-string">"boundaries"</span>:
                            <span class="hljs-keyword">if</span> col <span class="hljs-keyword">in</span> self.tileset.keys():
                                surf = image.load(<span class="hljs-string">f'<span class="hljs-subst">{self.tileset[col]}</span>'</span>).convert_alpha()
                                Tile((x,y), [self.visible, self.obstacles], <span class="hljs-string">'boundary'</span>,surf) 
                        <span class="hljs-keyword">if</span> layer == <span class="hljs-string">"collectibles"</span>:
                            <span class="hljs-keyword">if</span> col <span class="hljs-keyword">in</span> self.tileset.keys():
                                surf = image.load(<span class="hljs-string">f'<span class="hljs-subst">{self.tileset[col]}</span>'</span>).convert_alpha()
                                <span class="hljs-comment">#y + 64-16 because the images are 16x16px not 64x64</span>
                                Tile((x,y+<span class="hljs-number">52</span>), [self.visible, self.obstacles], <span class="hljs-string">'collectible'</span>,surf)
                        <span class="hljs-keyword">if</span> layer == <span class="hljs-string">"hazards"</span>:
                            <span class="hljs-keyword">if</span> col <span class="hljs-keyword">in</span> self.tileset.keys():
                                surf = image.load(<span class="hljs-string">f'<span class="hljs-subst">{self.tileset[col]}</span>'</span>).convert_alpha()
                                <span class="hljs-comment">#y + 64-16 because the images are 16x16px not 64x64</span>
                                Hazard((x,y+<span class="hljs-number">32</span>), [self.visible, self.obstacles], <span class="hljs-string">'hazard'</span>,surf)
                        <span class="hljs-keyword">if</span> layer == <span class="hljs-string">"enemies"</span>:
                            <span class="hljs-keyword">if</span> col <span class="hljs-keyword">in</span> self.tileset.keys():
                                <span class="hljs-comment">#y + 64-16 because the images are 16x16px not 64x64</span>
                                enemy = Enemy((x,y+<span class="hljs-number">52</span>), [self.visible, self.obstacles], <span class="hljs-string">'enemy'</span>,self.tileset[col], self.player, self.obstacles)

                        <span class="hljs-keyword">if</span> layer == <span class="hljs-string">"portal"</span>:
                            <span class="hljs-keyword">if</span> col <span class="hljs-keyword">in</span> self.tileset.keys():
                                surf = image.load(<span class="hljs-string">f'<span class="hljs-subst">{self.tileset[col]}</span>'</span>).convert_alpha()
                                <span class="hljs-comment">#y + 64-16 because the images are 16x16px not 64x64</span>
                                Portal((x,y+<span class="hljs-number">52</span>), [self.visible, self.obstacles], <span class="hljs-string">'portal'</span>,surf)

                        <span class="hljs-keyword">if</span> layer == <span class="hljs-string">"magic_objs"</span>:
                            print(col)
                            surf = image.load(<span class="hljs-string">f'assets\\magic_objs\\<span class="hljs-subst">{magic[col<span class="hljs-number">-1</span>]}</span>.png'</span>).convert_alpha()
                            <span class="hljs-comment">#y + 64 because the images are 16x16px not 64x64</span>
                            magic_object((x,y), [self.hidden], <span class="hljs-string">f'<span class="hljs-subst">{magic[col<span class="hljs-number">-1</span>]}</span>'</span>,surf)
</code></pre>
<p>You’ll notice that magic objects are not added to either of self.visible or self.obstacles, but rather to self.hidden. This is because we’ll reveal them with a special command, and then make them visible and collide able. Therefore they are hidden for the time being.</p>
<p>Now another interesting thing, is that enemies are not passed in the image as a surface, rather they are passed the path directly. This is because the enemies are animated, and their image is actually a spritesheet, not a single image. Later when we write our enemy class, we’ll write a function to break down this spritesheet and add animations but you can ignore that for now.</p>
<p>Let me explain what this line does : <code>surf = image.load(f'{self.tileset[col]}').convert_alpha()</code></p>
<p>Remember how we created a tileset corresponding firstgids to sources earlier? well, we use that same tileset to access the image path at ‘col’ , the value of the layer’s 2D array at that index, and we know this array contains GIDs of the tiles, so all we’re doing is getting the image for that specific tile on the map and passing it into the tile’s object to draw on the screen.</p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>I know this seems like a <em>lot</em> to take in but just experiment with the data file and you’ll see why and how our functions work. In the meantime, try working on animating the player and enemies yourself.<br />See you in part 2, happy coding!</p>
]]></content:encoded></item><item><title><![CDATA[Decoding the colors of stars]]></title><description><![CDATA[At first glance, you might think all the stars in the night sky are white. However, if you look closer, you'll see they display a variety of colors, including red, orange, yellow, white, and blue. These colors are visible indicators of a star’s prope...]]></description><link>https://webwidewit.com/decoding-the-colors-of-stars</link><guid isPermaLink="true">https://webwidewit.com/decoding-the-colors-of-stars</guid><category><![CDATA[astrophysics]]></category><category><![CDATA[Physics]]></category><category><![CDATA[color]]></category><dc:creator><![CDATA[Fatima Ali]]></dc:creator><pubDate>Sat, 01 Mar 2025 05:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1762303360604/1b94d655-de4d-4989-b982-3575073c0348.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>At first glance, you might think all the stars in the night sky are white. However, if you look closer, you'll see they display a variety of colors, including red, orange, yellow, white, and blue. These colors are visible indicators of a star’s properties, such as its surface temperature and composition. In this article, we’ll explore what causes stars to have the colors they do, and how astronomers use this to their benefit when analyzing cosmic events.</p>
<h3 id="heading-what-exactly-is-color">What exactly <em>is</em> color?</h3>
<p>We know color to be the result of electromagnetic radiation within a specific range of wavelengths (the visible spectrum), however, astronomers have a different definition. In astronomy, color is defined as <em>the difference between the magnitude of a star in one passband and the magnitude of the same star in a different passband.</em> This basically means that color (also referred to as the color index) is the difference between the brightness of a star when viewed from two different wavelength filters. The most common wavelength filters are ‘U’ (ultraviolet), ‘B’ (blue), ‘R’ (red), ‘V’ (visible), and when light is viewed through them, we get the brightness of the star in that specific wavelength range.</p>
<p><img src="https://i.ytimg.com/vi/FrfcjNTapCU/hq720.jpg?sqp=-oaymwEhCK4FEIIDSFryq4qpAxMIARUAAAAAGAElAADIQj0AgKJD&amp;rs=AOn4CLDtid27vuliy2rgGwj_6vBD2qePDw" alt="Electromagnetic Spectrum - Basic Introduction" class="image--center mx-auto" /></p>
<h3 id="heading-how-does-color-relate-to-temperature">How does color relate to temperature?</h3>
<p>The color of a star is a crucial indicator of its temperature. This relationship is based on the principles of <strong>black-body radiation</strong> and <strong>Wien's Law</strong>. Blackbody radiation describes the connection between an object's temperature and the wavelength of the electromagnetic radiation it emits. A black body is an ideal object that absorbs all the electromagnetic radiation it encounters. It then emits thermal radiation in a continuous spectrum based on its temperature.<br />Stars act similarly to blackbodies, which helps explain why they have different colors. Red stars are cooler and emit most of their radiation in the red wavelengths. In contrast, stars that appear blue or white are much hotter, and emit most of their radiation in ultraviolet/blue wavelengths.</p>
<p>We don't see any stars as green because stars with peak wavelengths in the green also emit a lot of radiation in the red and blue parts of the spectrum. Our eyes mix all these colors, and we perceive them as white. Much cooler objects, like planets and humans, emit most radiation in the infrared (<em>this is why body heat shows up on infrared cameras!</em>). Even cooler objects emit microwaves and radio waves.</p>
<p><img src="https://s3-us-west-2.amazonaws.com/courses-images/wp-content/uploads/sites/1095/2016/11/03155020/OSC_Astro_05_02_Illustrate.jpg" alt="Graph of radiation laws. The horizontal axis shows wavelength ranging from 1000 to 3000 nanometers. The vertical axis shows intensity in arbitrary units. Four different curves are shown, each corresponding to an object at a certain temperature in degrees Kelvin. The highest point of each curve is labeled with a dot that indicates the wavelength corresponding to the peak energy emitted by the object at that temperature. The 3000 K curve peaks at 1200 nm in the infrared. The 4000 K object peaks at 900 nm in the near-infrared, the 5000 K curve peaks at 700 nm in the visible-red, and the 6000 K object peaks at about 500 nm in the yellow part of the visible spectrum." /></p>
<p>As the diagram above shows, an <strong>increase</strong> in temperature leads to a <strong>decrease</strong> in wavelength.</p>
<p>Wien's Displacement Law states that the peak wavelength of emission from a blackbody is inversely proportional to its absolute temperature. In simple terms, as an object gets hotter, the color of its emitted light shifts toward shorter (bluer) wavelengths. Cooler objects radiate longer (redder) wavelengths. This is because hot objects have more energy (thermal), which allows them to give off a larger fraction of their energy at shorter wavelengths (higher energies) than cool objects. We can also describe our observation that hotter objects radiate more power at all wavelengths in a mathematical form. If we sum up the contributions from all parts of the electromagnetic spectrum, we obtain the total energy emitted by a blackbody, given by the <strong>Stefan-Boltzmann law :</strong></p>
<h3 id="heading-8j2qud3wnzgs8j2cjvcdkyfigbq">𝐸=𝑒𝜎𝑇⁴</h3>
<p>The table below gives some example colors and their associated temperatures :</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1762314041904/7162c3d5-cba7-4b2f-a866-32785cd1580a.jpeg" alt class="image--center mx-auto" /></p>
<h3 id="heading-color-indices">Color Indices</h3>
<p>To figure out a star's exact color, astronomers usually check out its brightness using filters that only let through certain colors of light. They use a set of filters that measure brightness in ultraviolet, blue, and yellow light. These filters are called U (for ultraviolet), B (for blue), and V (for visual, which is yellow). They let through light at around 360 nm, 420 nm, and 540 nm, respectively. The brightness through each filter is shown in magnitudes. The difference between two of these magnitudes, like between blue and visual (B–V), is known as a color index.</p>
<p>Astronomers have decided that in the UBV system, the ultraviolet, blue, and visual magnitudes are set so that a star like Vega, with a surface temperature of about 10,000 K, has a color index of 0. The B–V color indexes for stars can go from −0.4 for the really blue stars, which are around 40,000 K, to +2.0 for the really red ones, which are about 2000 K. The Sun's B–V index is about +0.65. Just remember, the B–V index is always calculated as the “bluer” color minus the “redder” one.<br />But, why is this so? Well, for a color index, let’s say B-V, a negative value implies that Vmag &gt; Bmag while a positive value implies that Vmag &lt; Bmag. Since magnitudes are inversely proportional to brightness, if B-V is negative, we can conclude that the brightness in B is <strong>greater</strong> than the brightness in V, since the magnitude in B is <strong>less than</strong> the magnitude in V. Similarly, if B-V is positive, it implies that the brightness in V is greater than the brightness in B, making it appear ‘redder’.</p>
<h3 id="heading-how-does-this-matter-in-astronomy">How does this matter in astronomy?</h3>
<p>A celestial body’s color is crucial in astronomy, because not only does it tell us a lot about its temperature, it also reveals the body’s chemical makeup, age, and even if there's interstellar dust making it look redder than it is. By creating color-magnitude diagrams from these indices, astronomers learn about how stars evolve and the history of star clusters and galaxies. In some cosmic mergers, changes in the color of electromagnetic radiation can also show the formation of heavy elements like gold and neodymium, giving us a glimpse into the chemical processes happening.</p>
<p>…And that concludes this article. Thank you for reading till the end, and happy learning!</p>
]]></content:encoded></item><item><title><![CDATA[Creating a basic JavaFX application]]></title><description><![CDATA[JavaFX is a Java library and a GUI toolkit to develop web and desktop applications that can be run on multiple platforms such as Linux, Windows, macOS, etc. It displays a valuable collection of graphics and various media APIs. For this very reason, t...]]></description><link>https://webwidewit.com/creating-a-basic-javafx-application</link><guid isPermaLink="true">https://webwidewit.com/creating-a-basic-javafx-application</guid><category><![CDATA[Java]]></category><category><![CDATA[graphics]]></category><dc:creator><![CDATA[Fatima Ali]]></dc:creator><pubDate>Fri, 17 May 2024 18:30:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1721991174474/07de0ff3-fdaf-486a-b520-fbf19b997905.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>JavaFX is a Java library and a GUI toolkit to develop web and desktop applications that can be run on multiple platforms such as Linux, Windows, macOS, etc. It displays a valuable collection of graphics and various media APIs. For this very reason, to combine graphics, animations, and UI control in applications JavaFX is preferred.</p>
<p><strong>Implementing a JavaFX application with IntelliJ IDEA</strong></p>
<p>~ ( IntelliJ IDEA ide). To get started, create a new project selecting JavaFX as the generator.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1672059347096/48363865-558a-436a-a41c-63be9a285d10.png" alt class="image--center mx-auto" /></p>
<p>Once you create the project, open the .java file and start writing your code after the JavaFX package import statement (<code>package com.example.javafx</code>;) Other than this, we also need to import the <code>javafx.application.Application</code> class which contains the <code>start()</code> method, the entry point of any JavaFX application as well as the <code>javafx.stage.Stage;</code> class which forms the parameter for the <code>start()</code> method.</p>
<p>A Stage object represents a window on the computer's screen. The stage that is passed as a parameter to <code>start()</code> is constructed by the system. It represents the main window of a program, and is often referred to as the "primary stage." A program can create other windows by constructing new objects of type Stage.</p>
<p>Once everything has been imported, we need to create a public class. Since this is going to be an application, our class needs to <code>extend</code> an application. Inside the class, there will be two methods. A main method (makes things easier to call) and a start method that is included in the <code>javafx.application.Application;</code> class. Your code should look something like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1672060405757/5eafbe3c-f897-4b26-9367-66c173d886d2.png" alt class="image--center mx-auto" /></p>
<p>Any element that we now create in the start method will be added to a <code>scene()</code> which will then be added to the <code>stage</code> that is passed to the <code>start()</code> method at the top. The stage, which creates a window on the screen can contain many components such as menus, buttons, input boxes or even drawing areas for 2d or 3d shapes.</p>
<p>Let's see how you can create some of these components:</p>
<h3 id="heading-1creating-shapes">1.Creating shapes :</h3>
<p>JavaFX provides a variety of shape classes like <code>Rectangle</code>, <code>Circle</code>, <code>Line</code>, and <code>Ellipse</code> that you can use to draw basic shapes. For example:</p>
<pre><code class="lang-java"><span class="hljs-keyword">import</span> javafx.application.Application;
<span class="hljs-keyword">import</span> javafx.scene.Scene;
<span class="hljs-keyword">import</span> javafx.scene.layout.Pane;
<span class="hljs-keyword">import</span> javafx.scene.shape.Circle;
<span class="hljs-keyword">import</span> javafx.scene.shape.Line;
<span class="hljs-keyword">import</span> javafx.scene.shape.Rectangle;
<span class="hljs-keyword">import</span> javafx.scene.shape.Ellipse;
<span class="hljs-keyword">import</span> javafx.stage.Stage;

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ShapeDemo</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Application</span> </span>{

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">start</span><span class="hljs-params">(Stage primaryStage)</span> </span>{
        <span class="hljs-comment">// Create shapes</span>
        Rectangle rectangle = <span class="hljs-keyword">new</span> Rectangle(<span class="hljs-number">50</span>, <span class="hljs-number">50</span>, <span class="hljs-number">100</span>, <span class="hljs-number">75</span>);
        rectangle.setStyle(<span class="hljs-string">"-fx-fill: lightblue;"</span>);

        Circle circle = <span class="hljs-keyword">new</span> Circle(<span class="hljs-number">200</span>, <span class="hljs-number">100</span>, <span class="hljs-number">50</span>);
        circle.setStyle(<span class="hljs-string">"-fx-fill: lightgreen;"</span>);

        Line line = <span class="hljs-keyword">new</span> Line(<span class="hljs-number">50</span>, <span class="hljs-number">200</span>, <span class="hljs-number">250</span>, <span class="hljs-number">200</span>);
        line.setStyle(<span class="hljs-string">"-fx-stroke: black;"</span>);

        Ellipse ellipse = <span class="hljs-keyword">new</span> Ellipse(<span class="hljs-number">150</span>, <span class="hljs-number">300</span>, <span class="hljs-number">80</span>, <span class="hljs-number">50</span>);
        ellipse.setStyle(<span class="hljs-string">"-fx-fill: lightcoral;"</span>);

        <span class="hljs-comment">// Create a layout container</span>
        Pane pane = <span class="hljs-keyword">new</span> Pane();
        pane.getChildren().addAll(rectangle, circle, line, ellipse);

        <span class="hljs-comment">// Create a Scene and set it on the Stage</span>
        Scene scene = <span class="hljs-keyword">new</span> Scene(pane, <span class="hljs-number">300</span>, <span class="hljs-number">400</span>);
        primaryStage.setScene(scene);
        primaryStage.setTitle(<span class="hljs-string">"JavaFX Shapes"</span>);
        primaryStage.show();
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{
        launch(args);
    }
}
</code></pre>
<p>Here:</p>
<p><strong>Rectangle</strong>: Created with position <code>(50, 50)</code> and size <code>(100, 75)</code>.</p>
<ul>
<li><p><strong>Circle</strong>: Centered at <code>(200, 100)</code> with a radius of <code>50</code>.</p>
</li>
<li><p><strong>Line</strong>: Drawn from <code>(50, 200)</code> to <code>(250, 200)</code>.</p>
</li>
<li><p><strong>Ellipse</strong>: Centered at <code>(150, 300)</code> with radii <code>80</code> and <code>50</code>.</p>
</li>
</ul>
<h3 id="heading-2buttons-and-events">2.Buttons and events :</h3>
<p>Buttons are triggers used to perform certain actions, that can be customized using event handlers. Let's look at an example:</p>
<pre><code class="lang-java"><span class="hljs-keyword">import</span> javafx.application.Application;
<span class="hljs-keyword">import</span> javafx.scene.Scene;
<span class="hljs-keyword">import</span> javafx.scene.control.Button;
<span class="hljs-keyword">import</span> javafx.scene.layout.VBox;
<span class="hljs-keyword">import</span> javafx.stage.Stage;

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ButtonDemo</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Application</span> </span>{

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">start</span><span class="hljs-params">(Stage primaryStage)</span> </span>{
        Button button = <span class="hljs-keyword">new</span> Button(<span class="hljs-string">"Click Me"</span>);
        button.setOnAction(e -&gt; button.setText(<span class="hljs-string">"Button Clicked!"</span>));

        VBox vbox = <span class="hljs-keyword">new</span> VBox(button);
        vbox.setSpacing(<span class="hljs-number">10</span>);

        Scene scene = <span class="hljs-keyword">new</span> Scene(vbox, <span class="hljs-number">200</span>, <span class="hljs-number">100</span>);
        primaryStage.setScene(scene);
        primaryStage.setTitle(<span class="hljs-string">"JavaFX Button"</span>);
        primaryStage.show();
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{
        launch(args);
    }
}
</code></pre>
<h3 id="heading-3creating-menus">3.Creating Menus</h3>
<p>A menu bar, commonly seen in navbars of websites, contains items in a certain order to make them more accessible. Here's how you can create one with file and edit menus :</p>
<pre><code class="lang-java"><span class="hljs-keyword">import</span> javafx.application.Application;
<span class="hljs-keyword">import</span> javafx.scene.Scene;
<span class="hljs-keyword">import</span> javafx.scene.control.*;
<span class="hljs-keyword">import</span> javafx.scene.layout.BorderPane;
<span class="hljs-keyword">import</span> javafx.stage.Stage;

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MenuDemo</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Application</span> </span>{

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">start</span><span class="hljs-params">(Stage primaryStage)</span> </span>{
        <span class="hljs-comment">// Create menu items</span>
        MenuItem newItem = <span class="hljs-keyword">new</span> MenuItem(<span class="hljs-string">"New"</span>);
        MenuItem openItem = <span class="hljs-keyword">new</span> MenuItem(<span class="hljs-string">"Open"</span>);
        MenuItem saveItem = <span class="hljs-keyword">new</span> MenuItem(<span class="hljs-string">"Save"</span>);
        MenuItem cutItem = <span class="hljs-keyword">new</span> MenuItem(<span class="hljs-string">"Cut"</span>);
        MenuItem copyItem = <span class="hljs-keyword">new</span> MenuItem(<span class="hljs-string">"Copy"</span>);
        MenuItem pasteItem = <span class="hljs-keyword">new</span> MenuItem(<span class="hljs-string">"Paste"</span>);

        <span class="hljs-comment">// Create menus</span>
        Menu fileMenu = <span class="hljs-keyword">new</span> Menu(<span class="hljs-string">"File"</span>, <span class="hljs-keyword">null</span>, newItem, openItem, saveItem);
        Menu editMenu = <span class="hljs-keyword">new</span> Menu(<span class="hljs-string">"Edit"</span>, <span class="hljs-keyword">null</span>, cutItem, copyItem, pasteItem);

        <span class="hljs-comment">// Create menu bar</span>
        MenuBar menuBar = <span class="hljs-keyword">new</span> MenuBar(fileMenu, editMenu);

        <span class="hljs-comment">// Create a layout container</span>
        BorderPane borderPane = <span class="hljs-keyword">new</span> BorderPane();
        borderPane.setTop(menuBar);

        <span class="hljs-comment">// Create a Scene and set it on the Stage</span>
        Scene scene = <span class="hljs-keyword">new</span> Scene(borderPane, <span class="hljs-number">400</span>, <span class="hljs-number">300</span>);
        primaryStage.setScene(scene);
        primaryStage.setTitle(<span class="hljs-string">"JavaFX Menu"</span>);
        primaryStage.show();
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{
        launch(args);
    }
}
</code></pre>
<p>To run these, simply compile and execute to see your window containing all the different components.</p>
<p>Thanks for reading, hope this taught you something new :)</p>
]]></content:encoded></item><item><title><![CDATA[How to detect exoplanets using the transit method]]></title><description><![CDATA[Exoplanets are planetary systems that lie far, far outside our own solar system. The problem with them being so far away is that we can’t visit them or see them directly with most telescopes. So then, how have astronomers detected over 6,000 of these...]]></description><link>https://webwidewit.com/how-to-detect-exoplanets-using-the-transit-method</link><guid isPermaLink="true">https://webwidewit.com/how-to-detect-exoplanets-using-the-transit-method</guid><dc:creator><![CDATA[Fatima Ali]]></dc:creator><pubDate>Mon, 01 Apr 2024 05:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1762579982610/828c6e91-d578-4f59-a4a4-4517cbe292b0.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Exoplanets are planetary systems that lie far, far outside our own solar system. The problem with them being so far away is that we can’t visit them or see them directly with most telescopes. So then, how have astronomers detected over 6,000 of these planets orbiting distant stars already? The answer lies in <em>analyzing data</em> collected from telescopes and spacecrafts to find information that directly or indirectly confirms their existence.</p>
<p>One of the newer, and more efficient ways to do this is via the <strong>Transit method</strong>. In this article, I’ll go over how the Transit method works, and one of the best algorithms used to filter true transits from raw data- the Box Least Squares algorithm!</p>
<h3 id="heading-the-transit-method">The Transit Method</h3>
<p>This technique involves watching the brightness of stars over time and looking for the small <em>dips</em> caused when an orbiting planet passes in front of its host star, blocking some of its light.</p>
<p><img src="https://svs.gsfc.nasa.gov/vis/a010000/a013000/a013022/Exoplanet_Single_print.jpg" alt /></p>
<p>The transit produces a characteristic light curve (a graph of brightness versus time) with a slight, periodic dip in intensity caused by the orbiting exoplanet each time it blocks the part of the star observed by the telescope. The depth and duration of these dips provide important information about the planet’s size and orbit. A deeper drop usually indicates a larger planet, while the distance between drops gives us the exoplanet's orbital period.</p>
<p><img src="https://svs.gsfc.nasa.gov/vis/a010000/a013000/a013022/Exoplanet_Double_print.jpg" alt /></p>
<p>Furthermore, we can use Kepler's third law of planetary motion to determine the distance of the exoplanet from its star. This information could give an idea as to whether the planet might possibly be in its host star's habitable zone (<em>regions where the physical conditions may be just right for liquid water</em>!).</p>
<p>Astronomers gather this data from space telescopes like NASA’s Kepler and TESS missions, which collect vast amounts of light intensity measurements over months and years. The raw data can be noisy due to instrumental effects or stellar activity, which is where computational methods help filter out true transits.</p>
<h3 id="heading-box-least-squares-bls">Box Least Squares (BLS)</h3>
<p>One popular technique to do this is the <strong>Box Least Squares (BLS)</strong> algorithm. It works by dividing the light curve into equal sections, and phase-folding them (<em>folding them on top of each other to fit within a mathematically determined period</em>) to fit a simple box-shaped model. It tries many different possible values for this orbital period until finding one that results in the signals (dips in brightness) aligning.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:1400/1*2ellU5g1V-2qreAsHw0JtA.png" alt="Box-Least-Square method. For the past few days, I have been… | by Jigar  Bhanderi | Medium" /></p>
<h3 id="heading-implementing-bls-using-astropy-and-matplotlib">Implementing BLS using Astropy and Matplotlib</h3>
<p>Now that we know how this algorithm works, we can move on to analyzing some data ourselves! For this article, I’m using <a target="_blank" href="https://www.kaggle.com/datasets/vijayveersingh/kepler-and-tess-exoplanet-data">Kepler &amp; TESS Exoplanet Data</a> from Kaggle that contains information about confirmed exoplanets (<em>since I’ll need a transit event to actually occur for me to demonstrate BLS</em>). I’m not going to be plotting raw telescope data, rather, using these known planetary periods and radii I’ll <strong>simulate</strong> how a star's brightness would look if these planets were passing in front of it.</p>
<p>Let’s start by importing all the external libraries we’ll need,</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> numpy <span class="hljs-keyword">as</span> np
<span class="hljs-keyword">import</span> matplotlib.pyplot <span class="hljs-keyword">as</span> plt
<span class="hljs-keyword">import</span> pandas <span class="hljs-keyword">as</span> pd
<span class="hljs-keyword">from</span> astropy.timeseries <span class="hljs-keyword">import</span> BoxLeastSquares
</code></pre>
<p>Then, we can write a function to simulate a light curve from the planet’s data as shown below.</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">simulate_lightcurve</span>(<span class="hljs-params">period, radius, n_points=<span class="hljs-number">1000</span></span>):</span>
    time = np.linspace(<span class="hljs-number">0</span>, max(period)*<span class="hljs-number">3</span>, n_points)
    flux = np.ones_like(time)

    <span class="hljs-keyword">for</span> p, r <span class="hljs-keyword">in</span> zip(period, radius):
        depth = (r * <span class="hljs-number">0.009158</span>)** <span class="hljs-number">2</span>
        duration = <span class="hljs-number">0.1</span> * p
        phase = (time % p) / p
        in_transit = (phase &lt; duration/p)
        flux[in_transit] -= depth

    <span class="hljs-keyword">return</span> time, flux
</code></pre>
<p>This function generates an array <code>time[]</code> that covers three orbits of the planet with the longest period and initializes the normalized brightness (flux) as 1. Then, for each planet it calculates the expected transit depth using the formula-</p>
<p><img src="https://avanderburg.github.io/tutorial/rprs.png" alt="Transit Light Curve Tutorial" class="image--center mx-auto" /></p>
<p><code>phase = (time%p) / p</code> folds the continuous timeline into repeating cycles between 0 and the period <code>p</code>. <code>in_transit</code> is a Boolean array that contains all time points that fall within the transit window. We then subtract the depth of the transit from the flux array at times when the light curve is <em>in</em> the transit event. Finally, we return the synthetic combined light curve representing all planets’ transits.</p>
<p>We can then write another function called <code>bls_implementation()</code> that extracts the period and radius from the Kaggle dataset and calls <code>simulate_lightcurve()</code> using them as parameters.</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">bls_implementation</span>(<span class="hljs-params">csv_path, max_planets=<span class="hljs-number">5</span></span>):</span>
    <span class="hljs-comment"># Load kaggle data</span>
    df = pd.read_csv(csv_path, comment=<span class="hljs-string">'#'</span>)

    <span class="hljs-comment"># Extract planets with valid orbital period and radius (Earth radii)</span>
    planets = df[[<span class="hljs-string">'pl_orbper'</span>, <span class="hljs-string">'pl_rade'</span>]].dropna()

    <span class="hljs-comment"># Filter realistic values</span>
    mask = (planets[<span class="hljs-string">'pl_orbper'</span>] &gt; <span class="hljs-number">0</span>) &amp; (planets[<span class="hljs-string">'pl_rade'</span>] &gt; <span class="hljs-number">0</span>)
    planets = planets[mask]

    <span class="hljs-comment"># Limit to first 'max_planets' for simulation clarity</span>
    sample = planets.head(max_planets)
    periods = sample[<span class="hljs-string">'pl_orbper'</span>].values
    radii = sample[<span class="hljs-string">'pl_rade'</span>].values

    <span class="hljs-comment"># Simulate combined light curve for selected planets</span>
    time, flux = simulate_lightcurve(periods, radii, n_points=<span class="hljs-number">5000</span>)
</code></pre>
<p>Here, <code>pl_orbper</code> and <code>pl_rade</code> are column names in the csv file that correspond to a planet’s orbital period and radius.</p>
<p>Now for the BLS implementation, we can use Astropy’s BoxLeastSquares function as such :</p>
<pre><code class="lang-python">    flux = flux / np.nanmedian(flux)

    <span class="hljs-comment"># Compute BLS periodogram</span>
    bls = BoxLeastSquares(time, flux)
    periods_grid = np.linspace(<span class="hljs-number">0.5</span>, <span class="hljs-number">1.5</span>*max(periods), <span class="hljs-number">20000</span>)
    results = bls.power(periods_grid, <span class="hljs-number">0.1</span>)

    <span class="hljs-comment"># Find best period peak</span>
    best_idx = np.argmax(results.power)
    best_period = periods_grid[best_idx]
    best_power = results.power[best_idx]
</code></pre>
<p>We first normalizes the brightness data, so all the flux values are centered around 1 by dividing by their median. This makes it easier to detect subtle dips caused by planetary transits. Then, we apply the Box Least Squares (BLS) algorithm to the normalized light curve over a range of possible orbital periods, searching for the best-fitting periodic box-shaped dip which represents the transit signal of a planet passing in front of its star. The code finds the period with the highest detection power, which is likely the planet’s orbital period.<br />We have orbital period data in our CSV file, allowing us to visualize the expected transit. However, real-world exoplanet hunters don't have this luxury. That's why they rely on algorithms like BLS, which provide highly accurate period values. We'll demonstrate this accuracy by plotting the light curve we simulated (based on real exoplanet periods and radii) alongside the BLS model's prediction below.</p>
<pre><code class="lang-python">    <span class="hljs-comment"># Plot combined light curve &amp; BLS periodogram</span>
    best_t0 = results.transit_time[best_idx]
    best_dur = results.duration[best_idx]
    model = bls.model(time, best_period, best_dur, best_t0)

    fig, (ax1, ax2) = plt.subplots(<span class="hljs-number">2</span>,<span class="hljs-number">1</span>, figsize=(<span class="hljs-number">10</span>,<span class="hljs-number">7</span>), gridspec_kw={<span class="hljs-string">'height_ratios'</span>:[<span class="hljs-number">2</span>,<span class="hljs-number">1</span>]})
    ax1.plot(time, flux, <span class="hljs-string">'k-'</span>, markersize=<span class="hljs-number">2</span>, alpha=<span class="hljs-number">0.6</span>, label=<span class="hljs-string">'Simulated Flux'</span>)
    ax1.plot(time, model, <span class="hljs-string">'r-'</span>, lw=<span class="hljs-number">1.5</span>, label=<span class="hljs-string">'BLS Transit Model'</span>)
    ax1.axvspan(best_t0 - <span class="hljs-number">0.5</span>*best_dur, best_t0 + <span class="hljs-number">0.5</span>*best_dur, color=<span class="hljs-string">'r'</span>, alpha=<span class="hljs-number">0.1</span>)
    ax1.set_xlabel(<span class="hljs-string">'Time (days)'</span>)
    ax1.set_ylabel(<span class="hljs-string">'Normalized Flux'</span>)
    ax1.set_title(<span class="hljs-string">'Simulated Exoplanet Transit Light Curve'</span>)
    ax1.legend()

    ax2.plot(periods_grid, results.power, <span class="hljs-string">'k-'</span>)
    ax2.axvline(best_period, color=<span class="hljs-string">'purple'</span>, linestyle=<span class="hljs-string">'--'</span>, label=<span class="hljs-string">f'Best Period = <span class="hljs-subst">{best_period:<span class="hljs-number">.5</span>f}</span> d'</span>)
    ax2.set_xlabel(<span class="hljs-string">'Period (days)'</span>)
    ax2.set_ylabel(<span class="hljs-string">'BLS Power'</span>)
    ax2.legend()

    plt.tight_layout()
    plt.show()
</code></pre>
<p>Calling <code>bls_implementation()</code> on the Kaggle data should result in a plot that looks something like this :</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1762664080806/6ef14f2d-0baa-45df-85d5-32b1bfb099d5.png" alt class="image--center mx-auto" /></p>
<p>From the first plot, we see that the time intervals for the simulated curve and the BLS model overlap almost entirely! However, the depth of the transit is significantly lower in the model. One possible reason for this is that because the BLS algorithm fits a simplified "box-shaped" model and works with imperfect data, it often estimates a shallower (less deep) transit depth than the idealized simulated transit that perfectly uses known planet size and orbit like the one in our confirmed exoplanets catalog. Still, we see that BLS matches the intervals between transits almost exactly, confirming the orbital period and reliably pinpointing when each transit occurs!</p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>So, what does all this mean? Basically, by using real exoplanet data to simulate how these planets would block their stars, and then applying the BLS algorithm to detect those dips in light intensity, we can get an idea of how transit detection actually works. The simulation gives us a clear, ‘ideal' picture of the transits, while BLS finds those signals in messier data that has some noise (like the data you’d get from a telescope). Even though the depths of the transits might not perfectly match, BLS discerns the timing of their occurrence almost exactly, which is what really matters when confirming and discovering planets.</p>
<p>Thank you for reading! All the code can be found below :</p>
<p><a target="_blank" href="https://github.com/fa22991/bls_algorithm_for_exoplanet_detection">https://github.com/fa22991/bls_algorithm_for_exoplanet_detection</a></p>
]]></content:encoded></item><item><title><![CDATA[CSS Countdown]]></title><description><![CDATA[CSS, short for Cascading Style Sheets, is a web developer's go-to toolkit for styling and design. Just like algorithms in computer science, CSS employs unique techniques to achieve various visual goals. Here, I'll walk you through my top eight CSS ta...]]></description><link>https://webwidewit.com/css-countdown</link><guid isPermaLink="true">https://webwidewit.com/css-countdown</guid><category><![CDATA[CSS]]></category><category><![CDATA[animation]]></category><dc:creator><![CDATA[Fatima Ali]]></dc:creator><pubDate>Tue, 23 Jan 2024 14:41:51 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1706020755060/21ca273f-0ee9-419b-a21d-bad5f7482bb3.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>CSS, short for Cascading Style Sheets, is a web developer's go-to toolkit for styling and design. Just like algorithms in computer science, CSS employs unique techniques to achieve various visual goals. Here, I'll walk you through my top eight CSS tactics.</p>
<h3 id="heading-1-focus-hover-and-nth-child">1. Focus, Hover and nth-child</h3>
<p>Focus, Hover, and nth-child are a few of the most useful pseudo classes in CSS. A pseudo-class is a keyword added to a selector that specifies a special state of the selected element(s). Pseudo-classes allow you to apply styles to elements based on various conditions that cannot be represented by simple selectors alone.</p>
<p>1. Hover. <code>:hover</code> allows you to change the style of the element when the user's mouse hovers over it.<br />2. Focus. <code>:focus</code> allows you to change the style of the selected element when an element is in focus, and the style remains as long as the element is in focus.<br />3. nth-child. <code>:nth-child(n)</code> allows you to change the style of element "n" contained in the parent block.</p>
<p>These classes are demonstrated below :</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codepen.io/fatima28/pen/rNRzGpM">https://codepen.io/fatima28/pen/rNRzGpM</a></div>
<p> </p>
<p>As shown above, the nth-child class is given "odd" as a parameter, allowing it to change the color of the odd-numbered spans contained inside the section (keep in mind it does not depend on the TEXT contained in the spans). Instead, you can pass in even or any number of your choice.</p>
<h3 id="heading-2-transitions-and-animations">2. Transitions and animations</h3>
<p>In CSS, transitions and animations are powerful tools for adding dynamic and visually appealing effects to web elements. Transitions enable smooth changes in property values over a specified duration, offering a seamless transition between different states. For instance, you can use transitions to smoothly alter the color or size of an element when a user hovers over it. On the other hand, animations provide more complex and customizable motion effects. CSS animations allow you to define keyframes that describe the style changes at different points in time, creating more intricate and dynamic movements. Whether it's a subtle fade-in effect or a complex sequence of transformations, transitions and animations enhance the user experience by bringing life and interactivity to web interfaces, making them more engaging and responsive.<br />The following clip demonstrates the above :</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codepen.io/fatima28/pen/XWGazJe">https://codepen.io/fatima28/pen/XWGazJe</a></div>
<p> </p>
<p>As shown above, <code>transition:</code> , allows for a gradual change in the <code>background-color</code> attribute of the button instead of a harsh one. Some transitions are demonstrated below:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codepen.io/fatima28/pen/poRaXoJ">https://codepen.io/fatima28/pen/poRaXoJ</a></div>
<p> </p>
<p>The <code>@keyframes</code> rule defines an animation named "rotate" that gradually rotates an element 360 degrees. The <code>.spinner</code> class applies this animation, creating a spinning effect. This animation is relatively simple, but you can create a lot more complex animations using CSS, which an article in the near-future might elaborate on.</p>
<h3 id="heading-3-3d-effects">3. 3d effects</h3>
<p>We can make use of the <code>box-shadow</code> and <code>text-shadow</code> provided by CSS to create 3d illusions. <code>box-shadow</code> as the name suggests allows you to add a shadow, or multiple, to an element such as a div or section, whereas <code>text-shadow</code> does the same for text inside tags. The following clip illustrates a few designs you can create :</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codepen.io/fatima28/pen/NWJvwep?editors=1100">https://codepen.io/fatima28/pen/NWJvwep?editors=1100</a></div>
<p> </p>
<p>At first glance, these effects may seem complex, but all you need to do is tweak the four parameters to get the effect you desire. Any text, or box, shadow is added as such :<br /><code>text/box-shadow : x y blur-radius color;</code><br />The first parameter determines the position of the shadow on the x axis, the second determines how far away from the y axis it will be, the third determines how far the shadow will be stretched out and the last determines the color. Using these four parameters, you can create any number of combinations to get the effect you need.</p>
<p>4. Translating and rotating</p>
<p>Translating and rotating in CSS is a simple yet impressive task, which can be done via <code>keyframes</code>. Both <code>translate</code> and <code>rotate</code> are enclosed in the transform attribute, as demonstrated below:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codepen.io/fatima28/pen/ZEPJvpm">https://codepen.io/fatima28/pen/ZEPJvpm</a></div>
<p> </p>
<h3 id="heading-5-translating-and-rotating-in-the-third-dimension">5. Translating and rotating in the third dimension</h3>
<p>CSS can be used to create 3d transforms as well. However, to do this, you have to define a <code>perspective</code> attribute of the body. In CSS, the <code>perspective</code> property is used in combination with 3D transformations to create a sense of depth and perspective for transformed elements. When applied to a parent container, the <code>perspective</code> property determines the distance between the viewer and the z=0 plane (the plane at which elements appear without any perspective). This allows for functions such as <code>translateX</code> and <code>translateY</code> etc.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codepen.io/fatima28/pen/qBvXpXE">https://codepen.io/fatima28/pen/qBvXpXE</a></div>
<p> </p>
<p>As the object approaches us, we can see it get bigger, which is a result of translating in the z axis.</p>
<h3 id="heading-6-filters-and-gradients">6. Filters and Gradients</h3>
<p>CSS filters enable the manipulation of visual content by applying a variety of effects. Filters such as <code>blur</code>, <code>brightness</code>, <code>contrast</code>, <code>grayscale</code>, <code>sepia</code>, and more can be used to alter the appearance of images and elements. For example, applying a <code>blur</code> filter can create a subtle or dramatic softening effect, while adjusting <code>brightness</code> and <code>contrast</code> allows for fine-tuning the overall visual impact. For example :</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codepen.io/fatima28/pen/LYBajGb">https://codepen.io/fatima28/pen/LYBajGb</a></div>
<p> </p>
<p>CSS gradients provide a way to smoothly transition between two or more colors, either horizontally, vertically, or radially. Gradients can be applied to backgrounds or even as text colors, creating visually appealing and dynamic designs. Linear gradients move from one color to another in a straight line, while radial gradients radiate from a central point.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codepen.io/fatima28/pen/xxJYxXG">https://codepen.io/fatima28/pen/xxJYxXG</a></div>
<p> </p>
<h3 id="heading-7-fonts-and-font-awesome">7. Fonts, and Font awesome</h3>
<p>While there are a limited number of inbuilt fonts in CSS, you can use <code>import</code> to import fonts from libraries on the internet, such as google fonts. For example:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codepen.io/fatima28/pen/dyrzJrN">https://codepen.io/fatima28/pen/dyrzJrN</a></div>
<p> </p>
<p>To get the import code for these fonts, head over to the google fonts webpage and copy the text from there.</p>
<p>Coming on to font awesome. Font Awesome is a true gem for web designers and has saved me much time and effort personally. To use, head over to cdnjs.com and copy the font awesome cdn and add it to your html. Or, add the following line inside the <code>&lt;head&gt;</code> section of your HTML page:</p>
<p><code>&lt;link rel="stylesheet" href="</code><a target="_blank" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"><code>https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css</code></a><code>"&gt;</code></p>
<p>Font Awesome is designed to be used with inline elements. The <code>&lt;i&gt;</code> and <code>&lt;span&gt;</code> elements are widely used for icons. You place Font Awesome icons by using the prefix <code>fa</code> and the icon's name. To find the icon of your choice, head over to Font Awesome and copy the imbed link, and add it to your html.</p>
<h3 id="heading-8-button-styles">8. Button styles</h3>
<p>In CSS, button styling is a fundamental aspect of creating visually appealing and interactive user interfaces. Buttons can be customized to match the overall design theme of a website, providing a cohesive and engaging user experience. CSS properties such as <code>background-color</code>, <code>color</code>, <code>border</code>, <code>padding</code>, and <code>box-shadow</code> allow developers to control the visual aspects of buttons. Hover and active states can be further enhanced using pseudo-classes like <code>:hover</code> and <code>:active</code>, enabling smooth transitions or color changes when users interact with the button. Additionally, CSS frameworks and methodologies like Flexbox and Grid can be employed for layout consistency and responsiveness. I have created a couple of button styles using the <code>::before</code> and <code>::after</code> pseudo classes, shown below.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codepen.io/fatima28/pen/XWpqpxR">https://codepen.io/fatima28/pen/XWpqpxR</a></div>
<p> </p>
<p>And that concludes this list!</p>
]]></content:encoded></item><item><title><![CDATA[Understanding web scraping]]></title><description><![CDATA[Web scraping, in the literal sense, means scraping data off the web. "Scraping" refers to the act of collecting data from websites by programmatically accessing and parsing the HTML or XML structure of web pages. However, keep in mind that some websi...]]></description><link>https://webwidewit.com/understanding-web-scraping</link><guid isPermaLink="true">https://webwidewit.com/understanding-web-scraping</guid><category><![CDATA[Python]]></category><category><![CDATA[web scraping]]></category><category><![CDATA[HTML5]]></category><category><![CDATA[CSS]]></category><category><![CDATA[BeautifulSoup]]></category><dc:creator><![CDATA[Fatima Ali]]></dc:creator><pubDate>Sat, 26 Aug 2023 17:00:39 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1688667172945/58a6321e-23a6-4e75-afe8-942df9945d1f.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Web scraping, in the literal sense, means scraping data off the web. "Scraping" refers to the act of collecting data from websites by programmatically accessing and parsing the HTML or XML structure of web pages. However, keep in mind that some websites may block scrapers and too many attempts or requests might get your IP address blocked.</p>
<p>Prerequisites - basic HTML and CSS</p>
<h3 id="heading-part-i-setting-up-web-scraping-libraries">PART I: Setting up web scraping libraries -</h3>
<ol>
<li><p>Installing requests (cmd)-<br /> <code>pip install requests</code></p>
</li>
<li><p>Installing lxml (cmd) -<br /> <code>pip install lxml</code></p>
</li>
<li><p>Installing BeautifulSoup (bs version 4) -<br /> <code>pip install bs4</code></p>
<p> You might need to restart your computer for these to start working.<br /> To use these in your programs, <code>import</code> them.</p>
</li>
</ol>
<h3 id="heading-part-ii-grabbing-titles-classes-and-images">PART II: Grabbing titles, classes and images -</h3>
<p>Here, we'll be using www.example.com for every demonstration. Make sure to include "http://" or "https://" depending on which one your site has.<br />Now, to begin, start by importing <code>requests</code>. Requests has a function called "get" which lets you "get" a response from a page.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> requests

response = requests.get(<span class="hljs-string">"http://www.example.com"</span>)

print(type(response)) <span class="hljs-comment">#prints the type of response</span>
print(response.text) <span class="hljs-comment">#prints the response text</span>
</code></pre>
<p>*If you face any issues running the above, check your firewall to make sure it isn't blocking python.</p>
<p>The code above prints two things. The type of the response, as well as the text contained within. The text, however, is returned simply as a string. This is where BeautifulSoup comes into play; parsing. That is, bs4 will help us obtain specific data from the site, using ids and classes etc.</p>
<p>To create our "soup" object, we use the BeautifulSoup class within the bs4 module; to demonstrate :</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> requests
<span class="hljs-keyword">import</span> bs4 

response = requests.get(<span class="hljs-string">"http://www.example.com"</span>)

soup = bs4.BeautifulSoup(response.text, <span class="hljs-string">"lxml"</span>) <span class="hljs-comment">#second parameter is the engine</span>
</code></pre>
<p>In our "soup" object, we've used the bs4 module's BeautifulSoup class to parse through the response text using the engine specified by the second parameter, that is, <code>lxml</code>. The lxml library helps sort through the data and grab classes, tags and ids etc.<br />Now, to grab specific elements or tags from the page, we can use something called "select()" and pass in the tag or element we want to select.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> requests
<span class="hljs-keyword">import</span> bs4

response = requests.get(<span class="hljs-string">"http://www.example.com"</span>)
soup = bs4.BeautifulSoup(response.text, <span class="hljs-string">"lxml"</span>) 
title = soup.select(<span class="hljs-string">"title"</span>)
</code></pre>
<p>Here, we're grabbing the content between the HTML "&lt;title&gt;" and "&lt;/title&gt;" tags. Grabbing paragraphs, headings, divs etc works the same way, for example if you wanted to select a link, you could just put in "a" inside the quotes instead.<br />However, this by default returns a list of the content, as well as the tag. To get rid of both, you can simply return the first index of the string and return just the content using "getText()" : <code>title = soup.select("title")[0].getText</code><br />If this doesn't work, you can use another approach:</p>
<p><code>element = soup.find('p') text = element.getText()</code></p>
<p>Grabbing a class is slightly different. Instead of selecting an HTML tag, we can instead access specific parts of the src using css attributes like "#id" and ".class"</p>
<p>Images, can be accessed the same way. ex -</p>
<p><code>image = soup.select('img')[0] source = img['src']</code></p>
<h3 id="heading-part-iii-building-the-goodreads-web-scraper">PART III: Building the Goodreads web scraper -</h3>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> requests
<span class="hljs-keyword">from</span> bs4 <span class="hljs-keyword">import</span> BeautifulSoup

<span class="hljs-comment"># URL of the Goodreads list you want to scrape</span>
url = <span class="hljs-string">"https://www.goodreads.com/list/show/1.Best_Books_of_the_21st_Century"</span>

<span class="hljs-comment"># Send an HTTP GET request to the URL</span>
response = requests.get(url)

<span class="hljs-comment"># Parse the HTML content of the page using Beautiful Soup</span>
soup = BeautifulSoup(response.content, <span class="hljs-string">"html.parser"</span>)

<span class="hljs-comment"># Find all the book containers on the page</span>
book_containers = soup.find_all(<span class="hljs-string">"tr"</span>, itemtype=<span class="hljs-string">"http://schema.org/Book"</span>)

<span class="hljs-comment"># Loop through each book container and extract title and author information</span>
<span class="hljs-keyword">for</span> container <span class="hljs-keyword">in</span> book_containers:
    title = container.find(<span class="hljs-string">"a"</span>, class_=<span class="hljs-string">"bookTitle"</span>).get_text(strip=<span class="hljs-literal">True</span>)
    author = container.find(<span class="hljs-string">"a"</span>, class_=<span class="hljs-string">"authorName"</span>).get_text(strip=<span class="hljs-literal">True</span>)
    print(<span class="hljs-string">f"Title: <span class="hljs-subst">{title}</span>\nAuthor: <span class="hljs-subst">{author}</span>\n"</span>)
</code></pre>
<p>In this example, we first import the necessary libraries: <code>requests</code> for making HTTP requests and <code>BeautifulSoup</code> for parsing HTML. We then send an HTTP GET request to the Goodreads URL and parse the HTML content using Beautiful Soup.</p>
<p>We use Beautiful Soup's <code>find_all</code> method to locate all the book containers on the page, which have the <code>itemtype</code> attribute set to <code>"</code><a target="_blank" href="http://schema.org/Book"><code>http://schema.org/Book</code></a><code>"</code>. Within each container, we extract the title and author information using the appropriate HTML tags and class names.</p>
<p>Finally, we loop through each book container and print out the extracted title and author information.</p>
<p>(P.S what do you call a spider that codes?<br />.... a web scraper)</p>
]]></content:encoded></item><item><title><![CDATA[PDFs, spreadsheets and CSV files - Python]]></title><description><![CDATA[PDFs (portable document format), CSVs (comma-separated value files) and spreadsheets are a few of the most popular file formats existing today. Python provides a couple of tools to efficiently manipulate, analyze and work with such formats.

CSV file...]]></description><link>https://webwidewit.com/pdfs-spreadsheets-and-csv-files-python</link><guid isPermaLink="true">https://webwidewit.com/pdfs-spreadsheets-and-csv-files-python</guid><category><![CDATA[Python]]></category><category><![CDATA[files]]></category><category><![CDATA[pdf]]></category><category><![CDATA[csv]]></category><category><![CDATA[spreadsheets]]></category><dc:creator><![CDATA[Fatima Ali]]></dc:creator><pubDate>Thu, 27 Apr 2023 11:12:09 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1682593910535/e559b59f-a357-4521-8f02-f0e18c783da7.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>PDFs (portable document format), CSVs (comma-separated value files) and spreadsheets are a few of the most popular file formats existing today. Python provides a couple of tools to efficiently manipulate, analyze and work with such formats.</p>
<ol>
<li><h3 id="heading-csv-files">CSV files</h3>
<p> A CSV file stores tabular data (numbers and text) in plain text. Each line of the file is a data record consisting of one or more fields, separated by commas. To work with such files, Python has a built-in module called 'csv'.</p>
<pre><code class="lang-python"> <span class="hljs-keyword">import</span> csv
 data = open(<span class="hljs-string">"colors.csv"</span>,encoding=<span class="hljs-string">"utf-8"</span>) <span class="hljs-comment">#opening the file</span>
 csv_data = csv.reader(data) <span class="hljs-comment">#reading from the file</span>
 data_lines = list(csv_data) <span class="hljs-comment">#creating a list</span>

 <span class="hljs-keyword">for</span> line <span class="hljs-keyword">in</span> data_lines[:<span class="hljs-number">5</span>]:
     print(line)
</code></pre>
<p> For example, the file we're working with, called 'colors.csv', contains three columns - the name of the colour, its hex code and its RGB value. The first few lines open the file, read data from it, and store the said data in a list. The for loop, however, is used to print individual rows from the list upto the 5th row (<code>data_line[:5]</code> splices the list to the 5th row).</p>
<p> But here comes another question. How could you get only the RGB values of each colour up to line 5?</p>
<pre><code class="lang-python"> <span class="hljs-keyword">for</span> line <span class="hljs-keyword">in</span> data_lines[:<span class="hljs-number">5</span>]:
     print(line[<span class="hljs-number">2</span>])
</code></pre>
<p> We know that the third column contains the RGB values. We also know that list indexing starts from index 0, making the index position of the third column 2. So, by replacing <code>line</code> with <code>line[2]</code>, we can access the values in the third column of the 5 rows.</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1682508802932/f0bb2ed7-c78a-4531-86f2-5c8ec3175bd3.png" alt class="image--center mx-auto" /></p>
<p> Since <code>data_lines[i]</code> (<em>i</em> because the value keeps changing as the loop progresses) is <code>line</code>, <code>line[2]</code> gives the same output as the above-mentioned.</p>
<p> Now imagine you wanted to store the hex values (2nd column) of all the rows in 'colors.csv'. To do this, we could start by creating an empty list, to which we could then append the hex values of each row as we go through them.</p>
<pre><code class="lang-python"> <span class="hljs-keyword">import</span> csv
 data = open(<span class="hljs-string">"colors.csv"</span>,encoding=<span class="hljs-string">"utf-8"</span>) <span class="hljs-comment">#opening the file</span>
 csv_data = csv.reader(data) <span class="hljs-comment">#reading from the file</span>
 data_lines = list(csv_data) <span class="hljs-comment">#creating a list</span>

 hex_values = []

 <span class="hljs-keyword">for</span> line <span class="hljs-keyword">in</span> data_lines[<span class="hljs-number">1</span>:]: <span class="hljs-comment">#Excluding the first row (labels) </span>
     hex_values.append(line[<span class="hljs-number">1</span>]) <span class="hljs-comment">#attaching each hex code to the list</span>

 print(hex_values)
</code></pre>
<p> Everything mentioned above dealt with <em>reading</em> data from a CSV file, but what if you wanted to <em>write</em> to one? To do that, you just need to change the 'mode' when you open the file from 'r' for reading to 'w' for writing, and use CSV 'writer'.</p>
<pre><code class="lang-python"> <span class="hljs-keyword">import</span> csv
 colors = open(<span class="hljs-string">'colors.csv'</span>,mode=<span class="hljs-string">'w'</span>,newline=<span class="hljs-string">''</span>)
 writer = csv.writer(colors,delimiter=<span class="hljs-string">','</span>)
 writer.writerows([[<span class="hljs-string">'red'</span>,<span class="hljs-string">'#ff0000'</span>,<span class="hljs-string">'rgb(255,0,255)'</span>],[<span class="hljs-string">'green'</span>,<span class="hljs-string">'#00ff00'</span>,<span class="hljs-string">'rgb(0,255,0)'</span>]])
 colors.close()
</code></pre>
<p> This piece of code overrides the current contents of 'colors' and writes 2 new rows instead - using 'writerows'. The code for modifying a CSV file is similar to that for reading from one. While creating the writer, the 'delimiter' parameter basically specifies what we want to use to separate our columns.<br /> If you don't want to override the current contents but do want to add rows to the file, changing the mode from 'w' to 'a' (for append) allows you to add content to the bottom of the CSV files without modifying any of its current contents.</p>
</li>
<li><h3 id="heading-pdf-files">PDF files</h3>
<p> To work with PDFs in Python, you can install the PyPDF2 library using the following pip command :</p>
<p> <code>pip install PyPDF2</code></p>
<p> <strong>Reading a PDF Document</strong></p>
<p> To read a PDF document using PyPDF2, you first need to open the document using the <code>PdfReader</code> class. Once you have opened the document, you can access its various properties, such as the number of pages or the document information.</p>
<pre><code class="lang-python"> <span class="hljs-keyword">import</span> PyPDF2

 <span class="hljs-keyword">with</span> open(<span class="hljs-string">'sample.pdf'</span>, <span class="hljs-string">'rb'</span>) <span class="hljs-keyword">as</span> pdf_file:
     pdf_reader = PyPDF2.PdfReader(pdf_file)

     <span class="hljs-comment"># Print the number of pages in the document</span>
     print(len(pdf_reader.pages))

     <span class="hljs-comment"># Print the document information</span>
     print(pdf_reader.metadata)
</code></pre>
<p> <strong>Extracting Text</strong></p>
<p> Extracting text from a PDF document is one of the most common tasks when working with PDF files. PyPDF2 provides a simple interface to extract text from a PDF document using the <code>PdfReader</code> class.</p>
<pre><code class="lang-python"> <span class="hljs-keyword">import</span> PyPDF2

 pdf_file = open(<span class="hljs-string">'sample.pdf'</span>, <span class="hljs-string">'rb'</span>)
 pdf_reader = PyPDF2.PdfReader(pdf_file)

 <span class="hljs-comment"># Extract text from the first page</span>
 page = pdf_reader.pages[<span class="hljs-number">0</span>]
 text = page.extract_text()

 print(text)
</code></pre>
<p> <strong>Merging Text</strong></p>
<p> Merging multiple PDF documents into a single document is another common task when working with PDF files. PyPDF2 provides a simple interface to merge PDF documents using the <code>PdfMerger</code> class.</p>
<pre><code class="lang-python"> <span class="hljs-keyword">import</span> PyPDF2

 pdf_merger = PyPDF2.PdfMerger()

 <span class="hljs-comment"># Add PDF documents to the merger</span>
 pdf_merger.append(<span class="hljs-string">'document1.pdf'</span>)
 pdf_merger.append(<span class="hljs-string">'document2.pdf'</span>)

 <span class="hljs-comment"># Merge the documents and save the output</span>
 <span class="hljs-keyword">with</span> open(<span class="hljs-string">'merged_document.pdf'</span>, <span class="hljs-string">'wb'</span>) <span class="hljs-keyword">as</span> output:
     pdf_merger.write(output)
</code></pre>
<p> The code above merges the two pdfs into one document which we've named 'merged_document.pdf'.</p>
<p> <strong>Encrypting a PDF Document</strong></p>
<p> Encrypting a PDF document is a great way to protect sensitive information. PyPDF2 provides a simple interface to encrypt a PDF document using the <code>PdfWriter</code> class.</p>
<pre><code class="lang-python"> <span class="hljs-keyword">import</span> PyPDF2

 pdf_file = open(<span class="hljs-string">'sample.pdf'</span>, <span class="hljs-string">'rb'</span>)
 pdf_reader = PyPDF2.PdfReader(pdf_file)

 pdf_writer = PyPDF2.PdfWriter()

 <span class="hljs-comment"># Copy the pages from the original document to the new document</span>
 <span class="hljs-keyword">for</span> page_num <span class="hljs-keyword">in</span> range(len(pdf_reader.pages)):
     pdf_writer.add_page(pdf_reader.pages[page_num])

 <span class="hljs-comment"># Set the encryption parameters</span>
 pdf_writer.encrypt(<span class="hljs-string">'password'</span>, <span class="hljs-string">'random'</span>, use_128bit=<span class="hljs-literal">True</span>)

 <span class="hljs-comment"># Save the encrypted document</span>
 <span class="hljs-keyword">with</span> open(<span class="hljs-string">'encrypted_document.pdf'</span>, <span class="hljs-string">'wb'</span>) <span class="hljs-keyword">as</span> output:
     pdf_writer.write(output)
</code></pre>
<p> This piece of code creates a new document (<em>encrypted_document.pdf</em>) to which it copies pages from the original document and then sets a password that restricts access to it. The <code>encrypt()</code> method of PyPDF2's <code>PdfWriter</code> class is used to add password-based encryption to a PDF document. The pdf_writer.encrypt() takes the password to put on the document, which in this case is 'random' and an optional <em>use_128bit</em> parameter which specifies whether to use a 40-bit or 128-bit encryption (128 if set to true, and 40 if not).</p>
</li>
<li><h3 id="heading-spreadsheets">Spreadsheets</h3>
<p> One of the most popular Python libraries for working with files is <code>openpyxl</code>, a library for working with Excel (.xlsx) files.</p>
<p> <strong>Installing openpyxl</strong></p>
<p> To install openpyxl, run the following command.</p>
<pre><code class="lang-python"> pip install openpyxl
</code></pre>
<p> <strong>Reading Data from a Spreadsheet</strong></p>
<p> To read data from the spreadsheet, the function <code>load_workbook</code> is used.</p>
<pre><code class="lang-python"> <span class="hljs-keyword">import</span> openpyxl

 <span class="hljs-comment"># Load the workbook</span>
 workbook = openpyxl.load_workbook(<span class="hljs-string">'sample.xlsx'</span>)

 <span class="hljs-comment"># Select the worksheet</span>
 worksheet = workbook.active

 <span class="hljs-comment"># Print the values of the cells in the first row</span>
 <span class="hljs-keyword">for</span> cell <span class="hljs-keyword">in</span> worksheet[<span class="hljs-number">1</span>]:
     print(cell.value)
</code></pre>
<p> In this code snippet, we first load the Excel file 'sample.xlsx' using the <code>load_workbook()</code> function, then select the active worksheet using the <code>active</code> attribute of the workbook object. Finally, we iterate over the cells in the first row of the worksheet using a for loop and print the value of each cell using its <code>value</code> attribute.</p>
<p> <strong>Writing Data to a Spreadsheet</strong></p>
<p> Next, let's look at how we can write data to an Excel file using openpyxl. Here's a code snippet that demonstrates how to create a new Excel file and write some data to it:</p>
<pre><code class="lang-python"> <span class="hljs-keyword">import</span> openpyxl

 <span class="hljs-comment"># Create a new workbook</span>
 workbook = openpyxl.Workbook()

 <span class="hljs-comment"># Select the worksheet</span>
 worksheet = workbook.active

 <span class="hljs-comment"># Write some data to the worksheet</span>
 worksheet[<span class="hljs-string">'A1'</span>] = <span class="hljs-string">'Name'</span>
 worksheet[<span class="hljs-string">'B1'</span>] = <span class="hljs-string">'Age'</span>
 worksheet[<span class="hljs-string">'A2'</span>] = <span class="hljs-string">'Alice'</span>
 worksheet[<span class="hljs-string">'B2'</span>] = <span class="hljs-number">25</span>

 <span class="hljs-comment"># Save the workbook</span>
 workbook.save(<span class="hljs-string">'output.xlsx'</span>)
</code></pre>
<p> In this code snippet, we first create a new Excel workbook using the <code>Workbook()</code> function. We then select the active worksheet using the <code>active</code> attribute of the workbook object. Finally, we write some data to the worksheet by assigning values to the appropriate cells using indexing and save the workbook as 'output.xlsx'. Note that we can use either letters or numbers to index the columns.</p>
<p> <strong>Modifying an Existing Spreadsheet</strong></p>
<p> Finally, let's look at how we can modify an existing Excel file using openpyxl. Here's a code snippet that demonstrates how to load an Excel file, modify some of its contents, and save the changes:</p>
<pre><code class="lang-python"> <span class="hljs-keyword">import</span> openpyxl

 <span class="hljs-comment"># Load the workbook</span>
 workbook = openpyxl.load_workbook(<span class="hljs-string">'sample.xlsx'</span>)

 <span class="hljs-comment"># Select the worksheet</span>
 worksheet = workbook.active

 <span class="hljs-comment"># Modify some of the cell values</span>
 worksheet[<span class="hljs-string">'A2'</span>] = <span class="hljs-string">'Bob'</span>
 worksheet[<span class="hljs-string">'B2'</span>] = <span class="hljs-number">30</span>

 <span class="hljs-comment"># Save the changes to the workbook</span>
 workbook.save(<span class="hljs-string">'sample.xlsx'</span>)
</code></pre>
<p> In this code snippet, we first load the Excel file 'sample.xlsx' using the <code>load_workbook()</code> function. Finally, we modify some of the cell values using indexing and save the changes to the workbook using the <code>save()</code> function.</p>
</li>
</ol>
<h3 id="heading-project-ideas-using-file-manipulation"><strong>Project Ideas using file manipulation</strong></h3>
<ol>
<li><p>Budget Analyzer: Create a program that reads in a CSV file containing income and expenses data and generates a summary report. Users can specify the date range for the report, and the program can calculate the total income, total expenses, and net income for the period.</p>
</li>
<li><p>Invoice Generator: Create a program that generates PDF invoices based on a CSV file containing customer information and order details. Users can customize the invoice format, including the logo, header, footer, and payment terms.</p>
</li>
<li><p>Movie Recommendation Engine: Create a program that reads in a CSV file containing movies' rating data and uses collaborative filtering to recommend movies to users. The program can generate a list of top-rated movies, similar movies, and personalized recommendations based on the user's previous ratings.</p>
</li>
</ol>
<div class="hn-embed-widget" id="files"></div>]]></content:encoded></item><item><title><![CDATA[Introduction to GUI using PyQt]]></title><description><![CDATA[PyQt is a Python library used for creating graphical user interfaces (GUIs) for desktop applications.
It is a set of Python bindings for the Qt application framework, which is a popular and powerful C++ GUI toolkit used for building cross-platform ap...]]></description><link>https://webwidewit.com/introduction-to-gui-using-pyqt</link><guid isPermaLink="true">https://webwidewit.com/introduction-to-gui-using-pyqt</guid><category><![CDATA[Python]]></category><category><![CDATA[PyQt6]]></category><category><![CDATA[graphics]]></category><category><![CDATA[classes]]></category><category><![CDATA[functions]]></category><dc:creator><![CDATA[Fatima Ali]]></dc:creator><pubDate>Wed, 15 Feb 2023 17:48:43 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1685767126517/3f9078c6-8f3d-4e07-a187-bc8217a96b0e.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>PyQt is a Python library used for creating graphical user interfaces (GUIs) for desktop applications.</p>
<p>It is a set of Python bindings for the Qt application framework, which is a popular and powerful C++ GUI toolkit used for building cross-platform applications. PyQt provides a range of modules and classes for creating graphical user interfaces, including buttons, labels, text boxes, and more.</p>
<p><strong>The ✨Logic✨</strong></p>
<p>Every PyQt application is built on two fundamental objects -</p>
<ol>
<li><p>QApplication -<br /> Deals with event-handling and initialization. It does all the stuff that happens under the hood and is mostly automatic.</p>
</li>
<li><p>MainWindow -</p>
<p> Handles graphic elements and logic, done manually.</p>
</li>
</ol>
<p>Graphical elements of PyQt are called 'Widgets' and are created by the user to be displayed on the screen, for example - buttons or textfields.</p>
<p><strong>The ✨Implementation✨</strong></p>
<p>If you've never worked with PyQt before, you need to install it by running the following command in windows command prompt(CMD) or powershell, and in the terminal for mac users.</p>
<p><code>pip install PyQt5</code></p>
<p>Once this is downloaded, fire up your IDE and start writing a basic GUI application.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> sys
<span class="hljs-keyword">from</span> PyQt5.QtWidgets <span class="hljs-keyword">import</span> QApplication, QWidget
<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">"__main__"</span>: <span class="hljs-comment">#checks if the script is being run as the main program</span>
    app = QApplication(sys.argv) <span class="hljs-comment">#takes a command line argument from the user</span>
    w = QWidget() <span class="hljs-comment">#creates the main widget</span>
    w.resize(<span class="hljs-number">300</span>,<span class="hljs-number">300</span>) <span class="hljs-comment">#sets the size of the window to 300x300</span>
    w.setWindowTitle(<span class="hljs-string">"Basic program1"</span>) <span class="hljs-comment">#sets the title for the widget</span>
    w.show() <span class="hljs-comment">#displays the window</span>
    sys.exit(app.exec_()) <span class="hljs-comment">#starts the Qt event loop and waits for user interaction to stop</span>
</code></pre>
<p>To start off, you need to import the sys module along with the required 'widgets' to create a window. The other lines basically create a window of size 300x300 named 'Basic program1'. Line-by-line explanations are given through comments in the code above.</p>
<p>If there are no command line arguments to be given, we can replace the line <code>app = QApplication(sys.argv)</code> with <code>app = QApplication([])</code> and thereby would not have to import the <code>sys</code> module.</p>
<p>Before we jump into making the actual program, look at some of the commonly used widgets :</p>
<ol>
<li><p>QLabel: A widget used to display text or an image.</p>
</li>
<li><p>QPushButton: A widget used to display a clickable button.</p>
</li>
<li><p>QLineEdit: A widget used to accept a single line of text input.</p>
</li>
<li><p>QTextEdit: A widget used to accept multiple lines of text input.</p>
</li>
<li><p>QCheckBox: A widget used to display a checkbox.</p>
</li>
<li><p>QRadioButton: A widget used to display a radio button.</p>
</li>
<li><p>QComboBox: A widget used to display a drop-down list of options.</p>
</li>
<li><p>QSpinBox: A widget used to display a spin box for numerical input.</p>
</li>
<li><p>QSlider: A widget used to display a slider for adjusting a value within a range.</p>
</li>
<li><p>QProgressBar: A widget used to display a progress bar.</p>
</li>
</ol>
<p>Now, for the actual calculator;</p>
<p>✨<strong>Building the calculator</strong>✨</p>
<p><strong>Part I: Base template -</strong></p>
<pre><code class="lang-python">
<span class="hljs-keyword">import</span> PyQt5.QtWidgets <span class="hljs-keyword">as</span> qtw
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MainWindow</span>(<span class="hljs-params">qtw.QWidget</span>):</span> <span class="hljs-comment">#where the widgets are added</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self</span>):</span>
        super().__init__()
        self.setWindowTitle(<span class="hljs-string">"Calculator"</span>)
        self.show()

app = qtw.QApplication([])
mw = MainWindow()
app.exec_()
</code></pre>
<p>First, the code imports the PyQt5 <code>QtWidgets</code> module containing all the required widgets and renames it as <code>qtw</code> for convenience.</p>
<p>Next, a new class named <code>MainWindow</code> is defined that inherits from the PyQt5 <code>QWidget</code> class. The <code>__init__</code> method of the <code>MainWindow</code> class calls the <code>__init__</code> method of its superclass using the <code>super()</code> function, which initializes the <code>MainWindow</code> object as a <code>QWidget</code>. All the visual elements of the program are defined here.</p>
<p>The <code>MainWindow</code> object is then created by calling its constructor with no arguments, which sets up the window with default size and properties. Finally, the <code>show()</code> method is called on the <code>MainWindow</code> object to display the window on the screen.</p>
<p>The <code>QApplication</code> object is created with an empty list <code>[]</code> as its argument. This is because <code>QApplication</code> is the top-level application object and requires a list of command-line arguments, but in this case, we have no command-line arguments to pass, so an empty list is used.</p>
<p>Finally, the <code>MainWindow</code> object is created and stored in the variable <code>mw</code>. The <code>exec_()</code> method of the <code>QApplication</code> object is called to start the event loop and run the application. This event loop waits for user events such as mouse clicks, key presses, and so on, and handles them as they occur.</p>
<p><strong>Part II : Layout -</strong></p>
<p>Whenever we create a new widget, since there is no specific layout for the program, Qt will not know where to put it. To specify the location of widgets, there are a few pre-made layouts that we can use, such as the QV (vertical layout), the QH (horizontal layout) and the QGrid Layout. For the main window, we'll use a QV layout, and for the inner calculator keypad, we'll create a grid layout.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> PyQt5.QtWidgets <span class="hljs-keyword">as</span> qtw
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MainWindow</span>(<span class="hljs-params">qtw.QWidget</span>):</span> <span class="hljs-comment">#where the widgets are added</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self</span>):</span>
        super().__init__()
        self.setWindowTitle(<span class="hljs-string">"Calculator"</span>)
        self.setLayout(qtw.QVBoxLayout())
        self.keypad()

        self.show()

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">keypad</span>(<span class="hljs-params">self</span>):</span>
        container = qtw.QWidget()
        container.setLayout(qtw.QGridLayout())

        self.result_field = qtw.QLineEdit()
        result = qtw.QPushButton(<span class="hljs-string">'Enter'</span>)
        clear = qtw.QPushButton(<span class="hljs-string">'Clear'</span>)
        b9 = qtw.QPushButton(<span class="hljs-string">'9'</span>)
        b8 = qtw.QPushButton(<span class="hljs-string">'8'</span>)
        b7 = qtw.QPushButton(<span class="hljs-string">'7'</span>)
        b6 = qtw.QPushButton(<span class="hljs-string">'6'</span>)
        b5 = qtw.QPushButton(<span class="hljs-string">'5'</span>)
        b4 = qtw.QPushButton(<span class="hljs-string">'4'</span>)
        b3 = qtw.QPushButton(<span class="hljs-string">'3'</span>)
        b2 = qtw.QPushButton(<span class="hljs-string">'2'</span>)
        b1 = qtw.QPushButton(<span class="hljs-string">'1'</span>)
        b0 = qtw.QPushButton(<span class="hljs-string">'0'</span>)
        plus = qtw.QPushButton(<span class="hljs-string">'+'</span>)
        mins = qtw.QPushButton(<span class="hljs-string">'-'</span>)
        mult = qtw.QPushButton(<span class="hljs-string">'*'</span>)
        div = qtw.QPushButton(<span class="hljs-string">'/'</span>)


app = qtw.QApplication([])
mw = MainWindow()
app.exec_()
</code></pre>
<p>As you can see in the code above, we've added a new function <code>keypad()</code> containing the buttons of our calculator. This was done just to give it a seperate layout.<br />In lines 6 and 11, we've set the layouts for the widgets using <code>.setLayout(qtw.Layouttype)</code> However, if you run this code, you can see that none of the widgets actually <em>appear</em> on the screen. This is because we haven't actually <em>added</em> the widgets to the layout, they're just... there. To add these to the layout, we need to understand how grid layouts work -</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1676473397657/0cd64fbb-65a4-4081-b55b-701a3a199d41.png" alt class="image--center mx-auto" /></p>
<p>In each of these 'Cells', we need to position our widgets to match the grid layout. The coordinates of the cell begin with 0,0 and so on. To add a widget in a cell, we use <code>addWidget('widget name',3,4,2,4)</code>, which takes in 5 parameters - the widget to be added, the coordinates and the <em>span</em> of the cell. This span is simply the number of rows and columns our cell stretches across; the 4th parameter being for the rows and the 5th for the columns. To implement this, add the following code after declaring all the widgets -</p>
<pre><code class="lang-python">container.layout().addWidget(self.result_field, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">1</span>, <span class="hljs-number">4</span>) 
container.layout().addWidget(result, <span class="hljs-number">1</span>, <span class="hljs-number">0</span>, <span class="hljs-number">1</span>, <span class="hljs-number">2</span>)
container.layout().addWidget(clear, <span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">1</span>, <span class="hljs-number">2</span>)
container.layout().addWidget(b9, <span class="hljs-number">2</span>, <span class="hljs-number">0</span>)
container.layout().addWidget(b8, <span class="hljs-number">2</span>, <span class="hljs-number">1</span>)
container.layout().addWidget(b7, <span class="hljs-number">2</span>, <span class="hljs-number">2</span>)
container.layout().addWidget(b6, <span class="hljs-number">3</span>, <span class="hljs-number">0</span>)
container.layout().addWidget(b5, <span class="hljs-number">3</span>, <span class="hljs-number">1</span>)
container.layout().addWidget(b4, <span class="hljs-number">3</span>, <span class="hljs-number">2</span>)
container.layout().addWidget(b3, <span class="hljs-number">4</span>, <span class="hljs-number">0</span>)
container.layout().addWidget(b2, <span class="hljs-number">4</span>, <span class="hljs-number">1</span>)
container.layout().addWidget(b1, <span class="hljs-number">4</span>, <span class="hljs-number">2</span>)
container.layout().addWidget(b0,<span class="hljs-number">5</span>,<span class="hljs-number">0</span>,<span class="hljs-number">1</span>,<span class="hljs-number">3</span>)
container.layout().addWidget(plus, <span class="hljs-number">2</span>,<span class="hljs-number">3</span>)
container.layout().addWidget(mins, <span class="hljs-number">3</span>, <span class="hljs-number">3</span>)
container.layout().addWidget(mult, <span class="hljs-number">4</span>, <span class="hljs-number">3</span>)
container.layout().addWidget(div, <span class="hljs-number">5</span>, <span class="hljs-number">3</span>)
self.layout().addWidget(container) <span class="hljs-comment">#adds all the widgets to the main window</span>
</code></pre>
<p>Notice how we have used the self keyword in both the declaration of the <code>result_field</code> as well as while adding it to the layout. This is just to make <code>result_field</code> accessible outside of this method, so that we can use the input from here and incorporate it into our final step, the calculator <strong>logic</strong>.</p>
<p><strong>Part III : The calculator Logic -</strong></p>
<p>Every Qt widget can send signals and has slots for recieving signals from other widgets.Whenever an event takes place, widgets send signals to other widgets. This is essentially how widgets communicate with each other.<br />For this calculator, we'll use the 'clicked' signal which is emitted when a button is clicked, and use the <code>setText()</code> slot to change the text of the <code>result_field</code> when it recieves the clicked signal from the buttons.<br />To start off, we'll need a method that changes the text field according to the button that is pushed. We can do this by associating a seperate function for each of the buttons. For example, if we press the button '5', it'll set the function <code>number(N)</code> to 5.<br />Moreover, we'll need to create seperate function for each operator, that uses the value passed to it to perform a specific operation on the numbers passed.<br />The two main things we need to do now are to update the text_field methodically once any button is pressed and to display the answer when the enter button is clicked.</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self</span>):</span>
    super().__init__()
    self.setWindowTitle(<span class="hljs-string">"Calculator"</span>)
    self.setLayout(qtw.QVBoxLayout())
    self.keypad()
    self.tnum = []
    self.fnum = []
</code></pre>
<p>Firstly, enter in the <code>__init__</code> method, define two lists using <code>self.</code> so that they can be accessed when objects are made.The first list, <code>tnum</code>, stores the numerical values passed to the widget slots in the form of strings inside the list. Similarly, the second list, <code>fnum</code>, stores the final typed in string, combining the operator and the digits.</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">keypad</span>(<span class="hljs-params">self</span>):</span>
    container = qtw.QWidget()
    container.setLayout(qtw.QGridLayout())

    self.result_field = qtw.QLineEdit()
    result = qtw.QPushButton(<span class="hljs-string">'Enter'</span>, clicked = self.resultf)
    clear = qtw.QPushButton(<span class="hljs-string">'AC'</span>,clicked = self.clearf)
    b9 = qtw.QPushButton(<span class="hljs-string">'9'</span>, clicked=<span class="hljs-keyword">lambda</span>: self.num_press(<span class="hljs-string">'9'</span>))
    b8 = qtw.QPushButton(<span class="hljs-string">'8'</span>,clicked=<span class="hljs-keyword">lambda</span>: self.num_press(<span class="hljs-string">'8'</span>))
    b7 = qtw.QPushButton(<span class="hljs-string">'7'</span>,clicked=<span class="hljs-keyword">lambda</span>: self.num_press(<span class="hljs-string">'7'</span>))
    b6 = qtw.QPushButton(<span class="hljs-string">'6'</span>,clicked=<span class="hljs-keyword">lambda</span>: self.num_press(<span class="hljs-string">'6'</span>))
    b5 = qtw.QPushButton(<span class="hljs-string">'5'</span>,clicked=<span class="hljs-keyword">lambda</span>: self.num_press(<span class="hljs-string">'5'</span>))
    b4 = qtw.QPushButton(<span class="hljs-string">'4'</span>,clicked=<span class="hljs-keyword">lambda</span>: self.num_press(<span class="hljs-string">'4'</span>))
    b3 = qtw.QPushButton(<span class="hljs-string">'3'</span>,clicked=<span class="hljs-keyword">lambda</span>: self.num_press(<span class="hljs-string">'3'</span>))
    b2 = qtw.QPushButton(<span class="hljs-string">'2'</span>,clicked=<span class="hljs-keyword">lambda</span>: self.num_press(<span class="hljs-string">'2'</span>))
    b1 = qtw.QPushButton(<span class="hljs-string">'1'</span>,clicked=<span class="hljs-keyword">lambda</span>: self.num_press(<span class="hljs-string">'1'</span>))
    b0 = qtw.QPushButton(<span class="hljs-string">'0'</span>,clicked=<span class="hljs-keyword">lambda</span>: self.num_press(<span class="hljs-string">'0'</span>))
    plus = qtw.QPushButton(<span class="hljs-string">'+'</span>,clicked=<span class="hljs-keyword">lambda</span>: self.func_press(<span class="hljs-string">'+'</span>))
    mins = qtw.QPushButton(<span class="hljs-string">'-'</span>,clicked=<span class="hljs-keyword">lambda</span>: self.func_press(<span class="hljs-string">'-'</span>))
    mult = qtw.QPushButton(<span class="hljs-string">'*'</span>,clicked=<span class="hljs-keyword">lambda</span>: self.func_press(<span class="hljs-string">'*'</span>))
    div = qtw.QPushButton(<span class="hljs-string">'/'</span>,clicked=<span class="hljs-keyword">lambda</span>: self.func_press(<span class="hljs-string">'/'</span>))
</code></pre>
<p>Secondly, inside the keypad function, we create a lambda function for each of the buttons, which passes the numerical value from the button to the num_press function. If you're getting an error around this part, it's because you haven't imported the lambda function. You can do that be writing the following statement at the top of your program - <code>from _ast import Lambda</code> . A lambda function is basically a short, one-time use function that you can use instead of creating full-length functions for each of the buttons. Here, we call four functions - <code>resultf()</code>, <code>clearf()</code>, <code>num_press()</code> and <code>func_press()</code> - which each perform a seperate function which we'll look at below.</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">num_press</span>(<span class="hljs-params">self,key</span>):</span>
    self.tnum.append(key)
    tstring = <span class="hljs-string">''</span>.join(self.tnum)
    <span class="hljs-keyword">if</span> self.fnum:
        self.result_field.setText(<span class="hljs-string">''</span>.join(self.fnum)+tstring)
    <span class="hljs-keyword">else</span>:
        self.result_field.setText(tstring)

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">func_press</span>(<span class="hljs-params">self,operator</span>):</span>
    tstring = <span class="hljs-string">''</span>.join(self.tnum)
    self.fnum.append(tstring)
    self.fnum.append(operator)
    self.tnum = []
    self.result_field.setText(<span class="hljs-string">''</span>.join(self.fnum))
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">resultf</span>(<span class="hljs-params">self</span>):</span>
    fstring = <span class="hljs-string">''</span>.join(self.fnum)+<span class="hljs-string">''</span>.join(self.tnum)
    rstring = eval(fstring)
    fstring += <span class="hljs-string">'='</span>
    fstring += str(rstring)
    self.result_field.setText(fstring)
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">clearf</span>(<span class="hljs-params">self</span>):</span>
    self.result_field.clear()
    self.tnum = []
    self.fnum = []
</code></pre>
<p>Add the following code after the part where you add the widgets to the layout.</p>
<p><strong>num_press() :</strong><br />The <code>num_press</code> function appends the value of the button we've pressed to the <code>tnum</code> list. For example we press 4, then the value of <code>tnum</code> becomes ['4']. It then creates a new string <code>tstring</code> to store the value of <code>tnum</code>, just so python doesn't start evaluating it like an integer, so that the written expression can be displayed in <code>result_field</code> first. It then has an if statement which basically just checks if a number or operator has been added to <code>fnum</code> before, and joins the new given number to <code>tstring</code> if so. Otherwise, it just sets the text of the result field to the given number.</p>
<p><strong>func_press() :</strong><br />The <code>func-press()</code> function joins the values of <code>tstring</code> (containing the numerical value) and the operator which is passed in. It then sets <code>tnum</code> back to an empty string so that another number can be typed in (or rather <em>pressed</em> in). Lastly, it sets the text field to <code>fnum</code>, which contains the final expression (number + operator) typed in by the user.</p>
<p><strong>resultf() :</strong><br />The <code>resultf()</code> function joins <code>fnum</code> (first number+operator) to the second number. It then creates a variable <code>rstring</code> that stores the evaluated form of the expression and adds an equal sign. Then, it sets the completed string <code>fstring</code> as the text field.</p>
<p><strong>clearf() :</strong><br />As the name <em>clearly</em> suggests, this function clears the result_field and resets both the lists.</p>
<p>✨<strong>Final product</strong>✨</p>
<p>The code for the final calculator is as follows :</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> _ast <span class="hljs-keyword">import</span> Lambda
<span class="hljs-keyword">import</span> PyQt5.QtWidgets <span class="hljs-keyword">as</span> qtw
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MainWindow</span>(<span class="hljs-params">qtw.QWidget</span>):</span> <span class="hljs-comment">#where the widgets are added</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self</span>):</span>
        super().__init__()
        self.setWindowTitle(<span class="hljs-string">"Calculator"</span>)
        self.setLayout(qtw.QVBoxLayout())
        self.keypad()
        self.tnum = []
        self.fnum = []

        self.show()

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">keypad</span>(<span class="hljs-params">self</span>):</span>
        container = qtw.QWidget()
        container.setLayout(qtw.QGridLayout())

        self.result_field = qtw.QLineEdit()
        result = qtw.QPushButton(<span class="hljs-string">'Enter'</span>, clicked = self.resultf)
        clear = qtw.QPushButton(<span class="hljs-string">'AC'</span>,clicked = self.clearf)
        b9 = qtw.QPushButton(<span class="hljs-string">'9'</span>, clicked=<span class="hljs-keyword">lambda</span>: self.num_press(<span class="hljs-string">'9'</span>))
        b8 = qtw.QPushButton(<span class="hljs-string">'8'</span>,clicked=<span class="hljs-keyword">lambda</span>: self.num_press(<span class="hljs-string">'8'</span>))
        b7 = qtw.QPushButton(<span class="hljs-string">'7'</span>,clicked=<span class="hljs-keyword">lambda</span>: self.num_press(<span class="hljs-string">'7'</span>))
        b6 = qtw.QPushButton(<span class="hljs-string">'6'</span>,clicked=<span class="hljs-keyword">lambda</span>: self.num_press(<span class="hljs-string">'6'</span>))
        b5 = qtw.QPushButton(<span class="hljs-string">'5'</span>,clicked=<span class="hljs-keyword">lambda</span>: self.num_press(<span class="hljs-string">'5'</span>))
        b4 = qtw.QPushButton(<span class="hljs-string">'4'</span>,clicked=<span class="hljs-keyword">lambda</span>: self.num_press(<span class="hljs-string">'4'</span>))
        b3 = qtw.QPushButton(<span class="hljs-string">'3'</span>,clicked=<span class="hljs-keyword">lambda</span>: self.num_press(<span class="hljs-string">'3'</span>))
        b2 = qtw.QPushButton(<span class="hljs-string">'2'</span>,clicked=<span class="hljs-keyword">lambda</span>: self.num_press(<span class="hljs-string">'2'</span>))
        b1 = qtw.QPushButton(<span class="hljs-string">'1'</span>,clicked=<span class="hljs-keyword">lambda</span>: self.num_press(<span class="hljs-string">'1'</span>))
        b0 = qtw.QPushButton(<span class="hljs-string">'0'</span>,clicked=<span class="hljs-keyword">lambda</span>: self.num_press(<span class="hljs-string">'0'</span>))
        plus = qtw.QPushButton(<span class="hljs-string">'+'</span>,clicked=<span class="hljs-keyword">lambda</span>: self.func_press(<span class="hljs-string">'+'</span>))
        mins = qtw.QPushButton(<span class="hljs-string">'-'</span>,clicked=<span class="hljs-keyword">lambda</span>: self.func_press(<span class="hljs-string">'-'</span>))
        mult = qtw.QPushButton(<span class="hljs-string">'*'</span>,clicked=<span class="hljs-keyword">lambda</span>: self.func_press(<span class="hljs-string">'*'</span>))
        div = qtw.QPushButton(<span class="hljs-string">'/'</span>,clicked=<span class="hljs-keyword">lambda</span>: self.func_press(<span class="hljs-string">'/'</span>))

        container.layout().addWidget(self.result_field, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">1</span>, <span class="hljs-number">4</span>)
        container.layout().addWidget(result, <span class="hljs-number">1</span>, <span class="hljs-number">0</span>, <span class="hljs-number">1</span>, <span class="hljs-number">2</span>)
        container.layout().addWidget(clear, <span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">1</span>, <span class="hljs-number">2</span>)
        container.layout().addWidget(b9, <span class="hljs-number">2</span>, <span class="hljs-number">0</span>)
        container.layout().addWidget(b8, <span class="hljs-number">2</span>, <span class="hljs-number">1</span>)
        container.layout().addWidget(b7, <span class="hljs-number">2</span>, <span class="hljs-number">2</span>)
        container.layout().addWidget(b6, <span class="hljs-number">3</span>, <span class="hljs-number">0</span>)
        container.layout().addWidget(b5, <span class="hljs-number">3</span>, <span class="hljs-number">1</span>)
        container.layout().addWidget(b4, <span class="hljs-number">3</span>, <span class="hljs-number">2</span>)
        container.layout().addWidget(b3, <span class="hljs-number">4</span>, <span class="hljs-number">0</span>)
        container.layout().addWidget(b2, <span class="hljs-number">4</span>, <span class="hljs-number">1</span>)
        container.layout().addWidget(b1, <span class="hljs-number">4</span>, <span class="hljs-number">2</span>)
        container.layout().addWidget(b0,<span class="hljs-number">5</span>,<span class="hljs-number">0</span>,<span class="hljs-number">1</span>,<span class="hljs-number">3</span>)
        container.layout().addWidget(plus, <span class="hljs-number">2</span>,<span class="hljs-number">3</span>)
        container.layout().addWidget(mins, <span class="hljs-number">3</span>, <span class="hljs-number">3</span>)
        container.layout().addWidget(mult, <span class="hljs-number">4</span>, <span class="hljs-number">3</span>)
        container.layout().addWidget(div, <span class="hljs-number">5</span>, <span class="hljs-number">3</span>)
        self.layout().addWidget(container)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">num_press</span>(<span class="hljs-params">self,key</span>):</span>
        self.tnum.append(key)
        tstring = <span class="hljs-string">''</span>.join(self.tnum)
        <span class="hljs-keyword">if</span> self.fnum:
            self.result_field.setText(<span class="hljs-string">''</span>.join(self.fnum)+tstring)
        <span class="hljs-keyword">else</span>:
            self.result_field.setText(tstring)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">func_press</span>(<span class="hljs-params">self,operator</span>):</span>
        tstring = <span class="hljs-string">''</span>.join(self.tnum)
        self.fnum.append(tstring)
        self.fnum.append(operator)
        self.tnum = []
        self.result_field.setText(<span class="hljs-string">''</span>.join(self.fnum))
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">resultf</span>(<span class="hljs-params">self</span>):</span>
        fstring = <span class="hljs-string">''</span>.join(self.fnum)+<span class="hljs-string">''</span>.join(self.tnum)
        rstring = eval(fstring)
        fstring += <span class="hljs-string">'='</span>
        fstring += str(rstring)
        self.result_field.setText(fstring)
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">clearf</span>(<span class="hljs-params">self</span>):</span>
        self.result_field.clear()
        self.tnum = []
        self.fnum = []


app = qtw.QApplication([])
mw = MainWindow()
app.setStyle(qtw.QStyleFactory.create(<span class="hljs-string">'Fusion'</span>))
app.exec_()
</code></pre>
<p>...and that was how to create a calculator with PyQt!</p>
<p>Github repository - <a target="_blank" href="https://github.com/fa22991/PyQt-Calculator">https://github.com/fa22991/PyQt-Calculator</a></p>
]]></content:encoded></item><item><title><![CDATA[A guide to colours on the web]]></title><description><![CDATA[Colors on the web are most commonly displayed through color codes, which are simply ways of representing the colors we see daily in a format that can be interpreted by computers. These colors may be specified as RGB triplets or Hexadecimal triplets, ...]]></description><link>https://webwidewit.com/a-guide-to-colours-on-the-web</link><guid isPermaLink="true">https://webwidewit.com/a-guide-to-colours-on-the-web</guid><category><![CDATA[color]]></category><category><![CDATA[RGB]]></category><category><![CDATA[CSS]]></category><category><![CDATA[hex ]]></category><dc:creator><![CDATA[Fatima Ali]]></dc:creator><pubDate>Sun, 22 Jan 2023 14:51:50 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1674312351320/787a9877-09db-48c8-b977-96928bfa84c9.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Colors on the web are most commonly displayed through color codes, which are simply ways of representing the colors we see daily in a format that can be interpreted by computers. These colors may be specified as <em>RGB triplets</em> or <em>Hexadecimal triplets</em>, which are basically 3 sets of values(in different formats) that form a certain shade. To understand <em>how</em> to create different colors using these formats, we must first look at what exactly these codes <em>are</em>.</p>
<h3 id="heading-rgb">RGB</h3>
<p>An RGB color is composed of 3 channels on computer screens. These 3 channels have a minimum intensity of 0 and a maximum intensity of 255; 0 being the darkest shade and 255 being the lightest. An RGB color value is specified with rgb(red, green, blue). For example, rgb(255,0,0) is rendered as the color red, as the red parameter is set to its highest value 255 and the others are set to 0.<br />Another thing to remember is that RGB colors are <em>Additive colors</em>, as they <em>combine</em> to form white, unlike <em>Subtractive colors</em> which, as the name suggests, subtract to form white.</p>
<p><img src="https://framework7.io/i/docs/color-picker-rgb-sliders.png" alt="Color Picker | Framework7 Documentation" /></p>
<p>If all the sliders are set to 0, the darkest shade, the color presented would be black. Similarly, if they were all set to 255, the lightest shade, the color would be white.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codepen.io/fatima28/pen/KKBQKax">https://codepen.io/fatima28/pen/KKBQKax</a></div>
<p> </p>
<p>In the example above, we have set the intensity of blue to 255 and the others to 0, turning the background of the page blue. You can create most of the color combinations by adjusting the light of these three colors appropriately, for example :</p>
<ul>
<li><p>red + green = yellow</p>
</li>
<li><p>red + blue = magenta</p>
</li>
<li><p>green + blue = cyan</p>
</li>
</ul>
<p>Changing the intensity of the triplet can help make the color <em>lighter</em> or <strong>darker</strong> as needed.</p>
<p><strong>Changing the opacity of RGB colors:</strong></p>
<p>In addition to the typical <code>rgb()</code> function, CSS provides a rgba(<code>)</code> function which helps to control the opacity of the color. The <code>rgba()</code> function defines colors using the Red-green-blue-alpha (RGBA) model, which is nothing but an extension of RGB color values.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codepen.io/fatima28/pen/yLqvLvw">https://codepen.io/fatima28/pen/yLqvLvw</a></div>
<p> </p>
<p>The code example above changes the background of our body to the color red with an opacity of 0.5.<br />This opacity can range from 0.0-1.0, 0 making the color transparent and 1 making the color fully opaque.</p>
<h3 id="heading-hexadecimal">Hexadecimal</h3>
<p>In general, hexadecimal values have a base of 16. They follow a decimal number system up to base '10' but, as soon as they reach 10 (the first non-single-digit number), they switch to alphabets. The hexadecimal numbering would look something like: <code>1 2 3 4 5 6 7 8 9 A B C D E F</code><br /><em>Note: The letters A-F are used to represent the numbers 10-15.</em><br />Hex codes are usually represented by a 'hashtag' followed by a pair of each of the Red, Green and Blue values.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1674399045050/9d9ea033-5311-42e1-915b-73663dfa97ff.png" alt class="image--center mx-auto" /></p>
<p>In the color example above, the first two characters of the hex code represent the intensity of red, the second pair of characters represent the intensity of the color green and lastly, the third pair represents the intensity of blue.</p>
<p><strong>How does the computer know which color to display using hex codes?</strong><br />All color is represented by mixing the amounts of red green and blue. The computer interprets the amount of red from a hex code by converting it into rgb values. Considering the hex code above, look at the interpretation below.</p>
<p><em>The value of red :</em><br /><em>'d8'</em> - 8 is at one's place and has the base 16, thus, we can multiply 8 with 16 to the power 0. 'd' on the other hand is simply the value 13 represented in base 16. Since d is at tens place and has the base 16, we multiply d with 16 to the power 1. Adding the value of both of these equations, we get the amount of red in the color.<br /><code>8 x 16^0 = 8</code><br /><code>13 x 16^1 = 208</code><br /><code>8 + 208 = 216</code><br />Thus, the concentration of red in the color #d8b3fc is 216.</p>
<p><em>The value of green :</em><br /><em>'b3'</em> - 3 is at one's place and has the base 16, thus, we can multiply 3 with 16 to the power 0. 'b' on the other hand is simply the value 11 represented in base 16. Since b is at tens place and has the base 16, we multiply b with 16 to the power 1. Adding the value of both of these equations, we get the amount of green in the color.<br /><code>3 x 16^0 = 3</code><br /><code>11 x 16^1 = 176</code><br /><code>3 + 176 = 179</code><br />Thus, the concentration of green in the color #d8b3fc is 179.</p>
<p><em>The value of blue :</em><br /><em>'fc'</em> - c is at one's place and has the base 16, thus, we can multiply the decimal value of c (that is 12) with 16 to the power 0. 'f' on the other hand is simply the value 15 represented in base 16. Since f is at tens place and has the base 16, we multiply f with 16 to the power 1. Adding the value of both of these equations, we get the amount of blue in the color.<br /><code>12 x 16^0 = 12</code><br /><code>15 x 16^1 = 240</code><br /><code>12 + 240 = 252</code><br />Thus, the concentration of blue in the color #d8b3fc is 252.</p>
<p>These 3 values of <em>Red</em>, <em>Green</em> and <em>Blue</em> combine to form the color - rgb(216,179,252), which as you might notice is the same color as #d8b3fc. This converted rgb value is used to display color.</p>
<p><strong>How can you convert RGB colors to Hexadecimal colors?</strong><br />Consider the RGB color (255,255,255). To convert this to hexadecimal, you would first have to convert the value of each separate color to base 16 and then concatenate the three together.<br /><em>Red, Green</em> and <em>Blue</em> - The value of red, green and blue is 255 in our example. We know that in every hex color, there are two places for each color. The tens place and the one's place. We start with first place (16^1) and move on accordingly.<br /><code>15 x 16^1 = 240</code><br /><code>15 x 16^0 = 15</code><br />Since at both positions for every color, the value is 15, we can represent it as #FF in hexadecimal as 15 is represented by the letter F. As the value of all the 3 colors is the same, we can simply write the color as #FFFFFF ( a pair of F for each color).</p>
<p><strong>Bonus: How to create CSS gradients using hex and rgb colors?  
</strong>CSS gradients let you transition smoothly between two or more colors. These transitions can be done Linearly (going up, down, left, right or diagonally), Radially (defined by their center) or Conically (rotated around a center pivot). The following code imbed displays the types of CSS gradients on the different divs.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codepen.io/fatima28/pen/xxJYxXG">https://codepen.io/fatima28/pen/xxJYxXG</a></div>
<p> </p>
<p><code>Div 1</code> uses a linear gradient that transitions (as specified) to the right. <code>Div 2</code> uses a radial gradient and we see the color transitioning from the <em>center</em>. <code>Div 3</code> uses a conic gradient which spreads the color evenly from the starting angle. If this starting angle is not specified, by default it is considered 0 (the center).</p>
]]></content:encoded></item><item><title><![CDATA[The best Sorting Algorithms and their implementation]]></title><description><![CDATA[So, What are Algorithms? Algorithms, in computer science, are sets of steps for a computer program to achieve a goal. Depending on this "goal", we have a bunch of different algorithms. One such type of algorithm is the sort algorithm. A Sorting Algor...]]></description><link>https://webwidewit.com/the-best-sorting-algorithms-and-their-implementation</link><guid isPermaLink="true">https://webwidewit.com/the-best-sorting-algorithms-and-their-implementation</guid><category><![CDATA[algorithms]]></category><category><![CDATA[sorting algorithms]]></category><category><![CDATA[Time Complexity]]></category><category><![CDATA[efficiency]]></category><dc:creator><![CDATA[Fatima Ali]]></dc:creator><pubDate>Sun, 15 Jan 2023 15:03:27 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1673676051135/824880fd-41d8-4934-8359-76a1b80fbf20.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>So, What are Algorithms? Algorithms, in computer science, are sets of steps for a computer program to achieve a goal. Depending on this "goal", we have a bunch of different algorithms. One such type of algorithm is the sort algorithm. <em>A Sorting Algorithm is used to rearrange a given array or list of elements according to a comparison operator on the elements.</em> These algorithms can have various procedures, creating the various sort algorithms we use today. A few of the most <em>efficient</em> algorithms are listed below :</p>
<h3 id="heading-quicksort">QuickSort</h3>
<p>First on the list, and most efficient, is the "QuickSort" Algorithm. QuickSort, is a Divide and Conquer algorithm, meaning that it selects an element as a pivot and consequently partitions the given array around the picked pivot. Now, how does QuickSort actually <em>sort</em> the array? Well, the first step would be to select a random element from the array, element x, which we'll consider the pivot. The algorithm first puts x at its correct position, then puts all the elements <em>smaller</em> than it before position x and similarly, puts all elements <em>greater</em> than it after position x.</p>
<p>There can be multiple ways to implement partition. The logic is quite straightforward, we start from the leftmost element, keeping track of the indexes of elements smaller than element x (our pivot element). While going through the array, if we find a smaller element, we swap that element with the element x. Otherwise, we ignore the current element and move to the next. Implementation of this method is provided below :</p>
<pre><code class="lang-cpp"><span class="hljs-comment">/* C++ implementation of QuickSort */</span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;bits/stdc++.h&gt;</span></span>

<span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> <span class="hljs-built_in">std</span>;

<span class="hljs-comment">// A utility function to swap two elements</span>

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">swap</span><span class="hljs-params">(<span class="hljs-keyword">int</span>* a, <span class="hljs-keyword">int</span>* b)</span>

</span>{
    <span class="hljs-keyword">int</span> t = *a;
    *a = *b;
    *b = t;
}

<span class="hljs-comment">/* This function takes the last element as a pivot, places
the pivot element at its correct position in sorted
array, and places all smaller (smaller than pivot)
to the left of the pivot and all greater elements to the right
of pivot */</span>

<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">partition</span><span class="hljs-params">(<span class="hljs-keyword">int</span> arr[], <span class="hljs-keyword">int</span> low, <span class="hljs-keyword">int</span> high)</span>

</span>{
    <span class="hljs-keyword">int</span> pivot = arr[high]; <span class="hljs-comment">// pivot</span>
    <span class="hljs-keyword">int</span> i
        = (low
           - <span class="hljs-number">1</span>); <span class="hljs-comment">// Index of the smaller element and indicates</span>
                 <span class="hljs-comment">// the right position of pivot found so far</span>

    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> j = low; j &lt;= high - <span class="hljs-number">1</span>; j++) {
        <span class="hljs-comment">// If the current element is smaller than the pivot</span>
        <span class="hljs-keyword">if</span> (arr[j] &lt; pivot) {
            i++; <span class="hljs-comment">// increment index of the smaller element</span>
            swap(&amp;arr[i], &amp;arr[j]);
        }
    }
    swap(&amp;arr[i + <span class="hljs-number">1</span>], &amp;arr[high]);
    <span class="hljs-keyword">return</span> (i + <span class="hljs-number">1</span>);

}

<span class="hljs-comment">/* The main function that implements QuickSort
arr[] --&gt; Array to be sorted,
low --&gt; Starting index,
high --&gt; Ending index */</span>

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">quickSort</span><span class="hljs-params">(<span class="hljs-keyword">int</span> arr[], <span class="hljs-keyword">int</span> low, <span class="hljs-keyword">int</span> high)</span>
</span>{
    <span class="hljs-keyword">if</span> (low &lt; high) {
        <span class="hljs-comment">/* pi is partitioning index, arr[p] is now
        at the right place */</span>

        <span class="hljs-keyword">int</span> pi = partition(arr, low, high);

        <span class="hljs-comment">// Separately sort elements before</span>
        <span class="hljs-comment">// partition and after partition</span>

        quickSort(arr, low, pi - <span class="hljs-number">1</span>);
        quickSort(arr, pi + <span class="hljs-number">1</span>, high);

    }
}
<span class="hljs-comment">/* Function to print an array */</span>

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">printArray</span><span class="hljs-params">(<span class="hljs-keyword">int</span> arr[], <span class="hljs-keyword">int</span> size)</span>
</span>{
    <span class="hljs-keyword">int</span> i;
    <span class="hljs-keyword">for</span> (i = <span class="hljs-number">0</span>; i &lt; size; i++)
        <span class="hljs-built_in">cout</span> &lt;&lt; arr[i] &lt;&lt; <span class="hljs-string">" "</span>;
    <span class="hljs-built_in">cout</span> &lt;&lt; <span class="hljs-built_in">endl</span>;

}

<span class="hljs-comment">// Driver Code</span>
<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span>
</span>{
    <span class="hljs-keyword">int</span> arr[] = { <span class="hljs-number">10</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>, <span class="hljs-number">1</span>, <span class="hljs-number">5</span> };

    <span class="hljs-keyword">int</span> n = <span class="hljs-keyword">sizeof</span>(arr) / <span class="hljs-keyword">sizeof</span>(arr[<span class="hljs-number">0</span>]);

    quickSort(arr, <span class="hljs-number">0</span>, n - <span class="hljs-number">1</span>);

    <span class="hljs-built_in">cout</span> &lt;&lt; <span class="hljs-string">"Sorted array: \n"</span>;

    printArray(arr, n);

    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;

}

<span class="hljs-comment">// This code is contributed by rathbhupendra (geeksforgeeks website)</span>
</code></pre>
<p><em>Lomuto Partition ⬆️</em></p>
<p><strong>Analysis of QuickSort</strong> :</p>
<p>The time taken by QuickSort in general can be written as follows :</p>
<p><em>T(n) = T(k) + T(n-k-1) + Θ(n)</em>**</p>
<p><strong>Worst Case Scenario</strong> - Θ(n²)<br /><strong>Best Case Scenario</strong> - Θ(nLogn)</p>
<p>Admittedly, the worst-case time compressibility of QuickSort, being Θ(n^2) is quite high. However, it is much quicker in practice as the worst-case scenario is rarely faced. QuickSort can be implemented in different ways by changing the choice of the pivot therefore, the worst case rarely occurs for a given type of data</p>
<hr />
<h3 id="heading-merge-sort">Merge Sort</h3>
<p>Merge sort, on the other hand, uses the "Divide and conquer" strategy. Diving an array into its consistent subarrays, It sorts these subarrays and then <em>merges</em> the subarrays together to form a completely sorted array.</p>
<p>Note: a "subarray" is just a fancy way of saying "a part of an array" or "a smaller section of an array".</p>
<p><strong>What are the <em>benefits</em> of using Merge sort?</strong><br />Merge sort is used to sort large arrays in meagre time. Another great thing about using merge sort is the fact that it is a <em>stable</em> sort. A stable sort is a sort that preserves the original order of the given array, which means that if it encounters two equal elements in the array, it will put the element that was first in the given array before the second equal element, therefore preserving the original sequence.</p>
<p><strong>Working process :</strong></p>
<p>Merge sort is basically an algorithm that continuously divides an array into smaller and smaller arrays until the array becomes empty or has only one element left.</p>
<p>For example, we're given an array consisting of 6 elements:</p>
<p><img src="https://www.programiz.com/sites/tutorial2program/files/merge-sort-example_0.png" alt="Merge Sort (With Code in Python/C++/Java/C)" /></p>
<p>The first step that the merge sort algorithm would implement would be to break this array into equal halves. This algorithm works <em>recursively</em> meaning that it keeps on dividing the array until it cannot be divided further. Once all the subarrays are divided and sorted, they are merged together to form a <em>completely sorted</em> array.</p>
<p><strong>The Algorithm for Merge Sort :</strong></p>
<blockquote>
<p>step 1: start</p>
<p>step 2: declare array and left, right, mid variable</p>
<p>step 3: perform the merge function.<br />    if left &gt; right<br />        return<br />    mid= (left+right)/2<br />    mergesort(array, left, mid)<br />    mergesort(array, mid+1, right)<br />    merge(array, left, mid, right)</p>
<p>step 4: Stop</p>
</blockquote>
<p><strong>Implementation for Merge Sort :</strong></p>
<pre><code class="lang-python"><span class="hljs-comment"># Python program for implementation of MergeSort</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">mergeSort</span>(<span class="hljs-params">arr</span>):</span>
    <span class="hljs-keyword">if</span> len(arr) &gt; <span class="hljs-number">1</span>:
         <span class="hljs-comment"># Finding the mid of the array</span>
        mid = len(arr)//<span class="hljs-number">2</span>
        <span class="hljs-comment"># splicing the list before the middle (therefore in case of an array     having an odd number of elements, the left halve gets more elements.</span>

        L = arr[:mid]
        <span class="hljs-comment"># splicing the list after the middle</span>
        R = arr[mid:]
        <span class="hljs-comment"># Sorting the first half by recursion</span>
        mergeSort(L)
        <span class="hljs-comment"># Sorting the second half by recursion</span>
        mergeSort(R)
        i = j = k = <span class="hljs-number">0</span>

        <span class="hljs-comment"># Copy data to temp arrays L[] and R[]</span>
        <span class="hljs-keyword">while</span> i &lt; len(L) <span class="hljs-keyword">and</span> j &lt; len(R):
            <span class="hljs-keyword">if</span> L[i] &lt;= R[j]: <span class="hljs-comment">#if the first element of the left halve is greater than the first element of the right halve</span>
                arr[k] = L[i]
                i += <span class="hljs-number">1</span>
            <span class="hljs-keyword">else</span>:
                arr[k] = R[j]
                j += <span class="hljs-number">1</span>
            k += <span class="hljs-number">1</span>

        <span class="hljs-comment"># Checking if any element was left</span>
        <span class="hljs-keyword">while</span> i &lt; len(L):
            arr[k] = L[i]
            i += <span class="hljs-number">1</span>
            k += <span class="hljs-number">1</span>

        <span class="hljs-keyword">while</span> j &lt; len(R):
            arr[k] = R[j]
            j += <span class="hljs-number">1</span>
            k += <span class="hljs-number">1</span>

<span class="hljs-comment"># Code to print the list</span>

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">printList</span>(<span class="hljs-params">arr</span>):</span>
    <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(len(arr)):
        print(arr[i], end=<span class="hljs-string">" "</span>)
    print()

<span class="hljs-comment"># Driver Code</span>
<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">'__main__'</span>:
    arr = [<span class="hljs-number">12</span>, <span class="hljs-number">11</span>, <span class="hljs-number">13</span>, <span class="hljs-number">5</span>, <span class="hljs-number">6</span>, <span class="hljs-number">7</span>]
    print(<span class="hljs-string">"Given array is"</span>, end=<span class="hljs-string">"\n"</span>)
    printList(arr)
    mergeSort(arr)
    print(<span class="hljs-string">"Sorted array is: "</span>, end=<span class="hljs-string">"\n"</span>)
    printList(arr)

<span class="hljs-comment"># This code is contributed by Mayank Khanna (Geeks for Geeks website)</span>
</code></pre>
<p><strong>Time Complexity :</strong></p>
<p><strong>Best, Worst and Average cases</strong>: As merge sort divides the array into two halves, sorts them and then merges them together in linear time, the time complexity for all three cases is <strong>θ(nlogn).</strong></p>
<hr />
<h3 id="heading-bubble-sort">Bubble Sort</h3>
<p><strong>Bubble Sort</strong> is the simplest sorting algorithm that works by repeatedly swapping the adjacent elements if they are in the wrong order (that is if the second element is smaller than the first). However, this algorithm is not suitable for large data sets as its average and worst-case time complexity is quite high.</p>
<p><strong>Working</strong> :</p>
<p>Bubble sort starts with the first element of the given array, which we call the 'bubble element. It then continues to swap that element with the adjacent elements if they are placed in the wrong order up until the end of the array. For example :</p>
<p><img src="https://www.simplilearn.com/ice9/free_resources_article_thumb/Bubble-Sort-Algorithm-Soni/working-of-bubble-sort-algorithm1png.png" alt="What is Bubble Sort Algorithm? Time Complexity &amp; Pseudocode | Simplilearn" /></p>
<p><strong>Algorithm for Bubble Sort</strong> :</p>
<blockquote>
<pre><code class="lang-python">begin BubbleSort(list)

   <span class="hljs-keyword">for</span> all elements of list
      <span class="hljs-keyword">if</span> list[i] &gt; list[i+<span class="hljs-number">1</span>]
         swap(list[i], list[i+<span class="hljs-number">1</span>])
      end <span class="hljs-keyword">if</span>
   end <span class="hljs-keyword">for</span>

   <span class="hljs-keyword">return</span> list

end BubbleSort
</code></pre>
</blockquote>
<p><strong>Implementation</strong> :</p>
<pre><code class="lang-python">
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">bubbleSort</span>(<span class="hljs-params">arr</span>):</span>
    n = len(arr)
    <span class="hljs-comment"># Traverse through all array elements</span>
    <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(n):
        <span class="hljs-comment"># Last i elements are already in place</span>
        <span class="hljs-keyword">for</span> j <span class="hljs-keyword">in</span> range(<span class="hljs-number">0</span>, n-i<span class="hljs-number">-1</span>):
            <span class="hljs-comment"># traverse the array from 0 to n-i-1 (end of the array)</span>
            <span class="hljs-comment"># Swap if the element found is greater than the next element</span>
            <span class="hljs-keyword">if</span> arr[j] &gt; arr[j+<span class="hljs-number">1</span>]:
                arr[j], arr[j+<span class="hljs-number">1</span>] = arr[j+<span class="hljs-number">1</span>], arr[j]
<span class="hljs-comment"># Driver code to test above</span>
<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">"__main__"</span>:
  arr = [<span class="hljs-number">5</span>, <span class="hljs-number">1</span>, <span class="hljs-number">4</span>, <span class="hljs-number">2</span>, <span class="hljs-number">8</span>]
  bubbleSort(arr)
  print(<span class="hljs-string">"Sorted array is:"</span>)
  <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(len(arr)):
      print(<span class="hljs-string">"%d"</span> % arr[i], end=<span class="hljs-string">" "</span>)
</code></pre>
<p><strong>Time Complexity</strong> :</p>
<p><strong>Worst and Average case</strong>: The worst and average cases occur when the array is reverse-sorted.</p>
<p><em>Total number of comparison (Worst case) =</em> <strong><em>n(n-1)/2</em></strong>*<br />Total number of swaps (Worst case) =* <strong><em>n(n-1)/2</em></strong></p>
<p>Therefore, the time complexity is <strong><em>O(N<sup>2</sup>)*</em></strong>.*</p>
<p><strong>Best Case</strong>: The best case for bubble sort is when the given array is already sorted. The time complexity for this is <strong><em>O(N)*</em></strong>.*</p>
<hr />
<h3 id="heading-selection-sort">Selection Sort</h3>
<p>The selection sort algorithm sorts an array by repeatedly finding the minimum element from the <em>unsorted</em> part and placing it at the front. ( Yes, at the beginning there is no "sorted part" so we traverse the entire array ).</p>
<p><strong>Working</strong> :</p>
<p>For this example, consider array of n=5 to be arr[] = {48,52,13,34,19}</p>
<p><em>First Pass</em> :<br />For the first pass in the selection sort algorithms, we traverse the array from index 0-4. As a result of this traversal (where we also simultaneously compare elements), we find that 13 is the lowest element. Replacing the element at index 0 (48) with 13,<br />The new array becomes - {13,52,48,34,19}</p>
<p><em>Second Pass</em> :<br />For the second pass, the algorithm skips over the sorted element (which is the element at position 0) and iterates over the remaining unsorted portion. It finds the smallest number and then places it before the unsorted section (i.e. after element[0]).<br />The new array becomes - {13,19,48,34,52}</p>
<p><em>Third Pass</em> :<br />For the third pass, the algorithm skips over the sorted elements (the elements at indexes 0 and 1) and iterates over the remaining unsorted position. It finds the smallest number from the unsorted portion and once again places it after the sorted portion and before the unsorted portion.<br />The new array becomes - {13,19,34,48,52}</p>
<p><em>Fourth pass</em> :<br />For the fourth pass, the algorithm skips over the sorted elements (elements at indexes 0, 1 and 2) and iterates over the unsorted elements. Fortunately, this 'unsorted' portion is already sorted as 48 is placed before 52. And there we have our fully sorted array -<br />{13,19,34,48,52}</p>
<p><strong>Implementation</strong> :</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> sys
A = [<span class="hljs-number">64</span>, <span class="hljs-number">25</span>, <span class="hljs-number">12</span>, <span class="hljs-number">22</span>, <span class="hljs-number">11</span>]
<span class="hljs-comment"># Traverse through all array elements</span>
<span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(len(A)):
    <span class="hljs-comment"># Find the minimum element in remaining</span>
    <span class="hljs-comment"># unsorted array</span>
    min_idx = i
    <span class="hljs-keyword">for</span> j <span class="hljs-keyword">in</span> range(i+<span class="hljs-number">1</span>, len(A)):
        <span class="hljs-keyword">if</span> A[min_idx] &gt; A[j]:
            min_idx = j   
    <span class="hljs-comment"># Swap the found minimum element with</span>
    <span class="hljs-comment"># the first element       </span>
    A[i], A[min_idx] = A[min_idx], A[i]

<span class="hljs-comment"># Driver code to test above</span>
<span class="hljs-keyword">print</span> (<span class="hljs-string">"Sorted array"</span>)
<span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(len(A)):
    print(<span class="hljs-string">"%d"</span> %A[i],end=<span class="hljs-string">" "</span>)
</code></pre>
<p><strong>Time Complexity</strong> :</p>
<p>In the selection sort algorithm, the time complexity is <strong>O(n<sup>2</sup>) in all three cases</strong>. This is because, in each step, we are required to find minimum elements so that they can be placed in the correct position. Once we trace the complete array, we will get our minimum element.</p>
<hr />
<p><strong>What is the time complexity of an algorithm? :</strong></p>
<p><em>Instead of measuring the actual time required in executing each statement in the code,</em> <strong><em>Time Complexity considers how many times each statement executes.</em></strong> </p>
<p><strong><em>O(nlogn) &gt; O(n²) &gt; O(n)</em></strong></p>
]]></content:encoded></item><item><title><![CDATA[CSS vs JavaScript Animations]]></title><description><![CDATA[CSS or Cascading Style Sheet as the name suggests is used to add style to a web page. Amongst other things, it can also be used to create animations as well as transitions effortlessly. On the other hand, JavaScript is used to make a web page or webs...]]></description><link>https://webwidewit.com/css-vs-javascript-animations</link><guid isPermaLink="true">https://webwidewit.com/css-vs-javascript-animations</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[CSS]]></category><category><![CDATA[animation]]></category><category><![CDATA[setInterval]]></category><category><![CDATA[2Articles1Week]]></category><dc:creator><![CDATA[Fatima Ali]]></dc:creator><pubDate>Sat, 07 Jan 2023 17:17:42 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1755538844185/141b8a0f-a373-4fcb-be4d-0f82c5684f16.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>CSS or Cascading Style Sheet as the name suggests is used to add style to a web page. Amongst other things, it can also be used to create animations as well as transitions effortlessly. On the other hand, JavaScript is used to make a web page or website interactive. JavaScript can also be used to create animations, however, you might be wondering which one is better. CSS or JavaScript? To know that, first we need to take a look at <em>creating</em> animations with CSS and JavaScript.</p>
<p><strong>CSS Animations</strong>:</p>
<p>In CSS, the <code>@keyframes</code> command is used to create animations. Note that <code>@keyframes</code> might not work on every browser. The table below specifies which version of different browsers can support <code>@keyframes</code> :</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1673067692488/016298da-cb61-434f-9b0b-debaa887aa33.png" alt class="image--center mx-auto" /></p>
<p>For example, consider the following code snippet:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codepen.io/fatima28/pen/abjBQvM">https://codepen.io/fatima28/pen/abjBQvM</a></div>
<p> </p>
<p><code>@keyframes</code> works by slowly transitioning from the beginning position or beginning styles to the modified positions or styles. To declare a keyframes animation, <code>@keyframes</code> is used along with the name of your animation. Inside the brackets, you'll have to declare the "from" which is the starting position and the "to" which is the final position. "From" and "to" are the equivalent of "0%" and "100%" which is also an alternative you can use.</p>
<p>If you want to create an animation that is a bit more complex, i.e. has a couple of different stages, you can use different percentages such as "20%", "50%" etc. which can apply different styles or different properties at different stages in your animation. Consider the code below which uses different stages to change the colour of a div:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codepen.io/fatima28/pen/XWBNLbx">https://codepen.io/fatima28/pen/XWBNLbx</a></div>
<p> </p>
<p>Notice the syntax used to give the animation properties to the div. <code>animation-name</code>, and <code>animation-duration</code>. The following table contains a few of the essential CSS animation properties.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1673087449792/ef1ecb14-1afa-4567-857d-feb854e10b00.png" alt class="image--center mx-auto" /></p>
<p>You can condense all of these properties into a single line using the <code>animation:</code> property with the following syntax :<br /><code>animation: name duration timing-function delay iteration-count direction fill-mode play-state;</code></p>
<p><strong>JavaScript</strong> :</p>
<p>JavaScript animations are a tad bit trickier. Consider the following piece of code, which we'll break down to understand how to build a JavaScript animation.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codepen.io/fatima28/pen/KKBadzZ">https://codepen.io/fatima28/pen/KKBadzZ</a></div>
<p> </p>
<p>In our HTML section, we've created a button and passed the method "move()" to it to be called on a user's click. In function move(), we've assigned the element "animate" which is a div to the constant "elem". Setting the initial position variable (pos) to 50, we then create a new frame every 5 milliseconds (using <code>setInterval(frame, 5)</code>). This will go on for as long as the position does not become 350. While the position is not 350, we increment the value of pos by 1. The only thing left to do is to assign the new positions to the element.</p>
<p><strong>Conclusion:</strong></p>
<p>While I agree that both CSS and Js are great in their own ways, there needs to be a clear winner. That winner, for me, would be <strong>CSS</strong>. CSS, although used mostly for simpler "one-shot" animations is much more efficient than JavaScript which, however, is still preferred for advanced and more complex animations. CSS can be used to create a wide variety of animations smoothly and seamlessly without any of the complex syntaxes that come with JavaScript (who wants to write that much code?!).</p>
]]></content:encoded></item><item><title><![CDATA[Working with lists in Python]]></title><description><![CDATA[A list in Python is a collection of items in a particular order, used to collect multiple units into a single variable making it easier to access and work with all together. A list is represented by square brackets - '[]' and contains comma-separated...]]></description><link>https://webwidewit.com/working-with-lists-in-python</link><guid isPermaLink="true">https://webwidewit.com/working-with-lists-in-python</guid><category><![CDATA[Python]]></category><category><![CDATA[python beginner]]></category><category><![CDATA[python lists]]></category><category><![CDATA[#2Articles1Week Challenge]]></category><dc:creator><![CDATA[Fatima Ali]]></dc:creator><pubDate>Tue, 03 Jan 2023 12:06:48 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1672675940570/92de4b82-7b28-4b2a-a929-25a6b46da5a9.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A list in Python is a collection of items in a particular order, used to collect multiple units into a single variable making it easier to access and work with all together. A list is represented by square brackets - '[]' and contains comma-separated values which can be accessed individually.</p>
<p>Now, you may be wondering what the point is in using lists when arrays are much faster. Well, that is because the elements stored in an array are homogeneous i.e. they have the same datatype, whereas a list can contain all sorts of data regardless of its data type, which makes it much more efficient to group certain information together.</p>
<p>~ <strong>Accessing elements in a list</strong></p>
<p>Since lists are <em>collections</em> of items, you can access any element in a list by telling python its position or <em>index</em>. Indexing always begins at 0. This is because an index is simply the distance of an element from the starting point. Since the first element is positioned at the start, the distance becomes 0 and therefore the index becomes 0. Take a look at the following code sample :</p>
<pre><code class="lang-python">new_list = [<span class="hljs-string">'Jake'</span>, <span class="hljs-string">'Amy'</span>, <span class="hljs-string">'Charles'</span>] <span class="hljs-comment">#note the square brackets</span>
print(new_list)
</code></pre>
<p>On running this, we get an output that looks something like this :</p>
<p>['Jake', 'Amy', 'Charles']</p>
<p>As you can see, Python returns its representation of a list, including the square brackets. To remove these and get the <em>actual values</em>, you can print each individual item in the list using its <em>index</em>.</p>
<pre><code class="lang-python">new_list = [<span class="hljs-string">'Jake'</span>,  <span class="hljs-string">'Amy'</span>,  <span class="hljs-string">'Charles'</span>]             
print(new_list[<span class="hljs-number">0</span>])
</code></pre>
<p>Since the element at index zero, that is the first element, is 'Jake', the output produced will be Jake.</p>
<p>If you don't want to manually go on printing the entire contents of a list by this method, you can use a for loop to iterate over each element and consequently print it.</p>
<pre><code class="lang-python">new_list = [<span class="hljs-string">'Jake'</span>,  <span class="hljs-string">'Amy'</span>,  <span class="hljs-string">'Charles'</span>]
<span class="hljs-keyword">for</span> name <span class="hljs-keyword">in</span> new_list:
    print(name)
</code></pre>
<p>The output produced would look something like this:</p>
<p>Jake<br />Amy<br />Charles</p>
<p><strong>~ List functions</strong></p>
<ol>
<li><p><strong>Adding Elements to a list</strong></p>
<p> a. To add elements to the end of a list, you can use the <code>append()</code> function.</p>
<pre><code class="lang-python"> orig_list = [<span class="hljs-string">'Jake'</span>,  <span class="hljs-string">'Amy'</span>,  <span class="hljs-string">'Charles'</span>] 
 new_list = [<span class="hljs-string">'Jake'</span>,  <span class="hljs-string">'Amy'</span>,  <span class="hljs-string">'Charles'</span>]   
 new_list.append(<span class="hljs-string">'Rosa'</span>)
 print(orig_list)
 print(new_list)
</code></pre>
<p> The output would look something like this :</p>
<p> <code>['Jake', 'Amy', 'Charles'] ['Jake', 'Amy', 'Charles', 'Rosa']</code></p>
</li>
</ol>
<p>b. You can also insert an element into a list at any given position using the <code>.insert()</code> function.</p>
<pre><code class="lang-python">orig_list = [<span class="hljs-string">'Jake'</span>,  <span class="hljs-string">'Amy'</span>,  <span class="hljs-string">'Charles'</span>] 
new_list = [<span class="hljs-string">'Jake'</span>,  <span class="hljs-string">'Amy'</span>,  <span class="hljs-string">'Charles'</span>]   
new_list.insert(<span class="hljs-number">0</span>,<span class="hljs-string">'Rosa'</span>)
print(orig_list)
print(new_list)
</code></pre>
<p>The output would be :</p>
<p><code>['Jake', 'Amy', 'Charles'] ['Rosa', 'Jake', 'Amy', 'Charles']</code></p>
<p>As you can see, the insert function opens up a space at index zero (as mentioned above) which is then filled up by 'Rosa', the element inserted into the list.</p>
<ol>
<li><p><strong>Removing Elements from a list</strong></p>
<p> a. Removing an element from a certain position</p>
<p> For this purpose, you can use the <code>del</code> statement.</p>
<pre><code class="lang-python"> orig_list = [<span class="hljs-string">'Jake'</span>,  <span class="hljs-string">'Amy'</span>,  <span class="hljs-string">'Charles'</span>] 
 new_list = [<span class="hljs-string">'Jake'</span>,  <span class="hljs-string">'Amy'</span>,  <span class="hljs-string">'Charles'</span>]   
 <span class="hljs-keyword">del</span> new_list[<span class="hljs-number">0</span>]
 print(orig_list)
 print(new_list)
</code></pre>
</li>
</ol>
<p>The above code removes the element at index 0, which is Jake, leaving us with ['Amy', 'Charles'].</p>
<p>b. Removing an element and re-using it<br />You can remove a certain element from a list using the <code>pop()</code> method and store the deleted element in a variable so that it can be reused.</p>
<pre><code class="lang-python">orig_list = [<span class="hljs-string">'Jake'</span>,  <span class="hljs-string">'Amy'</span>,  <span class="hljs-string">'Charles'</span>] 
new_list = [<span class="hljs-string">'Jake'</span>,  <span class="hljs-string">'Amy'</span>,  <span class="hljs-string">'Charles'</span>]   
popped = new_list.pop()
print(orig_list)
print(new_list)
</code></pre>
<p>By default, the element popped is that of the last index. However, you can pop an element off of any index by specifying it in between the brackets of <code>pop()</code> like : <code>new_list.pop(0)</code></p>
<p>c. Removing an element without knowing the index<br />If you want to remove an element from a list but do not know anything but its value, you can use the <code>remove()</code> function.</p>
<pre><code class="lang-python">orig_list = [<span class="hljs-string">'Jake'</span>,  <span class="hljs-string">'Amy'</span>,  <span class="hljs-string">'Charles'</span>] 
new_list = [<span class="hljs-string">'Jake'</span>,  <span class="hljs-string">'Amy'</span>,  <span class="hljs-string">'Charles'</span>]   
new_list.remove(<span class="hljs-string">'Amy'</span>)
print(orig_list)
print(new_list)
</code></pre>
<p>The code above, quite obviously, removes Amy from the list <code>new_list</code>.</p>
<p>Other common list methods include :</p>
<ol>
<li><p><strong>Sort()</strong> -</p>
<p> list.sort() is used to sort a list alphabetically.</p>
</li>
<li><p><strong>Reverse()</strong> -</p>
<p> list.reverse() is used to reverse a list.</p>
</li>
<li><p><strong>len()</strong> -</p>
<p> len(list) is used to get the length of a string which might be useful if you want to create a loop.</p>
</li>
</ol>
<p>And that was an introduction to lists in Python! Hope you learned something new and thanks for sticking around till the end.</p>
]]></content:encoded></item><item><title><![CDATA[Dry running a program: Everything you need to know]]></title><description><![CDATA[What does it mean to dry-run a program? Generally, a dry run refers to a process used to test something to ensure a proper output or response. For example, a company may conduct a 'dry run' test on a jet's new pilot ejection seat while the jet is par...]]></description><link>https://webwidewit.com/dry-running-a-program-everything-you-need-to-know</link><guid isPermaLink="true">https://webwidewit.com/dry-running-a-program-everything-you-need-to-know</guid><category><![CDATA[dry run]]></category><category><![CDATA[C]]></category><category><![CDATA[Programming Tips]]></category><category><![CDATA[running]]></category><dc:creator><![CDATA[Fatima Ali]]></dc:creator><pubDate>Sun, 04 Dec 2022 14:34:42 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1670513984228/IW0bPtrBw.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>What does it mean to dry-run a program? Generally, a <em>dry run</em> refers to a process used to test something to ensure a proper output or response. For example, a company may conduct a 'dry run' test on a jet's new pilot ejection seat while the jet is parked on the ground, rather than while it is in flight.</p>
<p>A dry run, in any programming language simply means to execute your code manually by going over each iteration, operation, and function to test it before the compilation and real execution of the program by a legit compiler.</p>
<p>Dry runs are usually performed for the following purposes:</p>
<ol>
<li>To mitigate the effects of possible failure of a piece of code</li>
<li>To validate a test case and improve it before documenting its 
 steps and assertions down.</li>
<li>To verify that a piece of code does not contain any bugs.</li>
<li>To identify and solve logical errors</li>
<li>To help understand code.</li>
</ol>
<p>Traditionally, a dry run would involve a programmer sitting down with a pen and paper and manually following the value of a variable to check that it was used and updated as expected. If the value is not what it is supposed to be, they can identify and correct the section of code that resulted in the errors.
For this example, consider the following piece of code that reverses a given number:</p>
<pre><code>#include &lt;stdio.h&gt;

int main() {

  int n, reverse = <span class="hljs-number">0</span>, remainder;

  printf(<span class="hljs-string">"Enter an integer: "</span>);
  scanf(<span class="hljs-string">"%d"</span>, &amp;n);

  <span class="hljs-keyword">while</span> (n != <span class="hljs-number">0</span>) {
    remainder = n % <span class="hljs-number">10</span>;
    reverse = reverse * <span class="hljs-number">10</span> + remainder;
    n /= <span class="hljs-number">10</span>;
  }

  printf(<span class="hljs-string">"Reversed number = %d"</span>, reverse);

}
</code></pre><p>We have three variables here, n, reverse and remainder. Let's track the value of each of these via a table.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1670160236130/vwFDyxhLM.png" alt="image.png" /></p>
<p>In our code, we have assigned the value of the user's input to the variable 'n'. Say that the integer we enter is '3456'. The variable n sets to 3456. Next, there is a while loop, where the condition is set to n!=0 meaning that the loop will run as long as n is not equal to zero. The variable 'remainder' is set to <em>n%10</em> which in this case is <em>3456%10</em>, which equals 6. The variable 'reverse' is set to <em>reverse x 10 + remainder</em>. 'Reverse' is initialized above to zero, hence the condition becomes <em>0x10+remainder</em>. Plugging in the value of 'remainder', we get r<em>everse = 0x10+6</em>, which equals 6 as well. Now, for the last statement in the while loop, <em>n/=10</em>. The '/=' operator is a shorthand operator which can be written alternatively as <em>n = n/10</em>. The updated value of n becomes 3456 (the original value of n) / 10, which evaluates to 345. Putting in the values of all our variables, our table should look something like this: </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1670161145909/KK2qclnPj.png" alt="image.png" /></p>
<p>The loop will not end here, however. As the value of n is 345 which satisfies the condition n!=0, the loop will run a couple more times. Since the loop runs once more, the statements inside it will run again as well. The variable 'remainder' is set to n%10, that is, 345%10, which evaluates to 5. The variable 'reverse' becomes 6 (the new value of reverse) x 10 + 'remainder', which evaluates to 60+5, that is 65. The variable n is updated to n/10, which is 345/10, which evaluates to 34.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1670162976046/sIhKNhWRz.png" alt="image.png" /></p>
<p>The value of n, which is 34, is still greater than zero. Thus, the loop runs once again. The variable 'remainder' is set to n%10, that is, 34%10, which evaluates to 4. The variable 'reverse' becomes 65 (the new value of reverse) x 10 + 'remainder', which evaluates to 650+4, that is 654. The variable n is updated to n/10, that is 34/10, which evaluates to 3. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1670163602663/UjUwaTIZh.png" alt="image.png" /></p>
<p>The value of n, which is 3, is still greater than zero. The loop runs one final time. The variable 'remainder' is set to n%10, that is, 3%10, which evaluates to 3. The variable 'reverse' becomes 654 (the new value of reverse) x 10 + 'remainder', which evaluates to 6540+3, that is 6543. The variable n is updated to n/10, that is 3/10, which evaluates to 0. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1670163789060/nW_JH6Hxb.png" alt="image.png" /></p>
<p>Now that the value of n has finally become zero, the loop ends as the condition evaluates to false. Therefore, the line after the loop, that is the print statement, will be executed. The output produced would look something like this: <em>Reversed number = 6543</em>. </p>
<p>And that was the end of this article! Hope you learned something new.</p>
]]></content:encoded></item><item><title><![CDATA[Strings in C]]></title><description><![CDATA[As you may know, C doesn't have a data type for declaring strings. This may seem odd to you if you know some other programming languages, that all have strings. C has a built-in '%s' operator which can be used to print strings, but how can you store ...]]></description><link>https://webwidewit.com/strings-in-c</link><guid isPermaLink="true">https://webwidewit.com/strings-in-c</guid><category><![CDATA[C]]></category><category><![CDATA[coding]]></category><category><![CDATA[Strings]]></category><dc:creator><![CDATA[Fatima Ali]]></dc:creator><pubDate>Fri, 02 Dec 2022 17:16:05 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1670079003691/Z2j-rjQc4.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>As you may know, C doesn't have a data type for declaring strings. This may seem odd to you if you know some other programming languages, that all have strings. C has a built-in '%s' operator which can be used to print strings, but how can you store strings in variables?
well, I can think of a few solutions :</p>
<ol>
<li><em>Array declaration</em> : 
In order to declare strings, you first need to understand what a string actually is. A string, is an array of characters, meaning that strings are actually chains of individual characters. You just need to declare an array of any size you want and assign it, the string value enclosed in double quotes.</li>
</ol>
<pre><code>#include &lt;stdio.h&gt;

int main (<span class="hljs-keyword">void</span>) 
{
char str[<span class="hljs-number">3</span>] = <span class="hljs-string">"Hi!"</span>;

printf(<span class="hljs-string">"%s\n"</span>,s);

}
</code></pre><p>The output would look like -
Hi!</p>
<p>Inputting a string is comparatively harder. You need to get memory from a library and use it to ask the user for a string.</p>
<pre><code>#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;

int main (<span class="hljs-keyword">void</span>) 
{

    char s[<span class="hljs-number">3</span>];
    <span class="hljs-keyword">for</span>(int i = <span class="hljs-number">0</span>; i&lt;<span class="hljs-number">3</span>; i++){
    s[i] = malloc(<span class="hljs-number">3</span>*(sizeof(char))); <span class="hljs-comment">//getting memory for 3 chars from the user</span>
    }
    scanf(<span class="hljs-string">"%s"</span>,s);
    printf(<span class="hljs-string">"%s\n"</span>,s);
    free(s);

}
</code></pre><ol>
<li><em>Pointer declaration</em>  :
In C, there are variables to store the address of certain things, like characters or integers. These pointers can help us store the <em>address</em> of characters, which form, you guessed it, a string. Now, how would you implement this in code? 
Well, a pointers is declared by '*', and a string stores a double quote value, so you might write this in code as -</li>
</ol>
<pre><code>#include &lt;stdio.h&gt;

int main (<span class="hljs-keyword">void</span>) 
{
char *s = <span class="hljs-string">"Hi!"</span>;
printf(<span class="hljs-string">"%s\n"</span>,s);

}
</code></pre><p>The output would look like -
Hi!</p>
<p>String input :</p>
<pre><code>#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;

int main(<span class="hljs-keyword">void</span>)
{
    char *s = malloc(<span class="hljs-number">3</span>*(sizeof(char))); <span class="hljs-comment">// getting as much memory required for 3 chars</span>
    scanf(<span class="hljs-string">"%s"</span>,s); <span class="hljs-comment">// getting the string</span>
    printf(<span class="hljs-string">"%s\n"</span>,s); <span class="hljs-comment">// printing the string</span>
    free(s); <span class="hljs-comment">// freeing the memory</span>
}
</code></pre><ol>
<li><em>Creating a string</em> : 
In my opinion,this is the easiest and most efficient way to deal with strings. You don't have a string data type? just create one! I know you're probably thinking that it's some advanced code but trust me, its not. To create your own data type, the function <em>typedef</em> is used. You use this (function?) and then the format which it replaces. </li>
</ol>
<pre><code>#include &lt;stdio.h&gt;

typedef char *string; <span class="hljs-comment">//string is the name you want to call the datatype</span>

int main(<span class="hljs-keyword">void</span>)
{
string s = <span class="hljs-string">"HI!"</span>;
printf(<span class="hljs-string">"%s\n"</span>,s);
}
</code></pre><p>To input a string :</p>
<pre><code>#include &lt;stdio.h&gt;

typedef char *string; <span class="hljs-comment">//string is the name you want to call the datatype</span>

int main(<span class="hljs-keyword">void</span>)
{
string s;
scanf(<span class="hljs-string">"%s"</span>,s);
printf(<span class="hljs-string">"%s\n"</span>,s);
}
</code></pre><p>And these are my 3 favorite ways to declare Strings!</p>
]]></content:encoded></item></channel></rss>