Building Jason Chess : Rendering the Board and Pieces

What Is All This?

Over quarantine, I’ve become very addicted to chess. I like how random chance plays a small role in the game. Instead, better players manifest from hours of obsessive study. Getting better at chess has been a great distraction from the world crumbling all around me, lol.

As an engineer, it’s my job to build solutions to problems, right? Right. Well I’ll identify one problem that exists: whenever I lose at chess. It’s my job to fix this. My solution: Jason Chess.

Jason Chess is a version of chess where if your name is “Jason”, you can’t lose.

This blog will follow the development of Jason Chess. It’s my goal to provide you with something instructive or at minimum, I hope you find the some humor in the idea.

I’m a junior developer so please forgive my imprecisions. If you have any feedback, please comment or to email me at jason.melton2@gmail.com.

Image for post

jump to the GitHub

Rendering the Board and Pieces

Table of Contents

  • Preliminary Junk
  • Basic Layout and Menu
  • Rendering the Board
  • Rendering the Pieces
  • Conclusion

Preliminary Junk

To get this bad boy started, I set up a create-react-app, deleted default junk, and created a GitHub repository. Before going further, I spent some time planning. I like to write as I plan so I’ll keep some commented out notes in the readme until the project is finished.

I think of the work in two parts: the logic and the render — very similar to the divide between a front and back end. However, in this case, I won’t have a true back end.

The logic of the chess board will be an array of eight arrays. Each array will have a length of eight to represent the 64 spaces of a chess board. Pieces will be coded with two character strings.

Image for post

The render will consist of mapping boardArr into the corresponding components.

#css #react #web-development #javascript

What is GEEK

Buddha Community

Building Jason Chess : Rendering the Board and Pieces
Beth  Cooper

Beth Cooper

1659694200

Easy Activity Tracking for Models, Similar to Github's Public Activity

PublicActivity

public_activity provides easy activity tracking for your ActiveRecord, Mongoid 3 and MongoMapper models in Rails 3 and 4.

Simply put: it can record what happens in your application and gives you the ability to present those recorded activities to users - in a similar way to how GitHub does it.

!! WARNING: README for unreleased version below. !!

You probably don't want to read the docs for this unreleased version 2.0.

For the stable 1.5.X readme see: https://github.com/chaps-io/public_activity/blob/1-5-stable/README.md

About

Here is a simple example showing what this gem is about:

Example usage

Tutorials

Screencast

Ryan Bates made a great screencast describing how to integrate Public Activity.

Tutorial

A great step-by-step guide on implementing activity feeds using public_activity by Ilya Bodrov.

Online demo

You can see an actual application using this gem here: http://public-activity-example.herokuapp.com/feed

The source code of the demo is hosted here: https://github.com/pokonski/activity_blog

Setup

Gem installation

You can install public_activity as you would any other gem:

gem install public_activity

or in your Gemfile:

gem 'public_activity'

Database setup

By default public_activity uses Active Record. If you want to use Mongoid or MongoMapper as your backend, create an initializer file in your Rails application with the corresponding code inside:

For Mongoid:

# config/initializers/public_activity.rb
PublicActivity.configure do |config|
  config.orm = :mongoid
end

For MongoMapper:

# config/initializers/public_activity.rb
PublicActivity.configure do |config|
  config.orm = :mongo_mapper
end

(ActiveRecord only) Create migration for activities and migrate the database (in your Rails project):

rails g public_activity:migration
rake db:migrate

Model configuration

Include PublicActivity::Model and add tracked to the model you want to keep track of:

For ActiveRecord:

class Article < ActiveRecord::Base
  include PublicActivity::Model
  tracked
end

For Mongoid:

class Article
  include Mongoid::Document
  include PublicActivity::Model
  tracked
end

For MongoMapper:

class Article
  include MongoMapper::Document
  include PublicActivity::Model
  tracked
end

And now, by default create/update/destroy activities are recorded in activities table. This is all you need to start recording activities for basic CRUD actions.

Optional: If you don't need #tracked but still want the comfort of #create_activity, you can include only the lightweight Common module instead of Model.

Custom activities

You can trigger custom activities by setting all your required parameters and triggering create_activity on the tracked model, like this:

@article.create_activity key: 'article.commented_on', owner: current_user

See this entry http://rubydoc.info/gems/public_activity/PublicActivity/Common:create_activity for more details.

Displaying activities

To display them you simply query the PublicActivity::Activity model:

# notifications_controller.rb
def index
  @activities = PublicActivity::Activity.all
end

And in your views:

<%= render_activities(@activities) %>

Note: render_activities is an alias for render_activity and does the same.

Layouts

You can also pass options to both activity#render and #render_activity methods, which are passed deeper to the internally used render_partial method. A useful example would be to render activities wrapped in layout, which shares common elements of an activity, like a timestamp, owner's avatar etc:

<%= render_activities(@activities, layout: :activity) %>

The activity will be wrapped with the app/views/layouts/_activity.html.erb layout, in the above example.

Important: please note that layouts for activities are also partials. Hence the _ prefix.

Locals

Sometimes, it's desirable to pass additional local variables to partials. It can be done this way:

<%= render_activity(@activity, locals: {friends: current_user.friends}) %>

Note: Before 1.4.0, one could pass variables directly to the options hash for #render_activity and access it from activity parameters. This functionality is retained in 1.4.0 and later, but the :locals method is preferred, since it prevents bugs from shadowing variables from activity parameters in the database.

Activity views

public_activity looks for views in app/views/public_activity.

For example, if you have an activity with :key set to "activity.user.changed_avatar", the gem will look for a partial in app/views/public_activity/user/_changed_avatar.html.(|erb|haml|slim|something_else).

Hint: the "activity." prefix in :key is completely optional and kept for backwards compatibility, you can skip it in new projects.

If you would like to fallback to a partial, you can utilize the fallback parameter to specify the path of a partial to use when one is missing:

<%= render_activity(@activity, fallback: 'default') %>

When used in this manner, if a partial with the specified :key cannot be located it will use the partial defined in the fallback instead. In the example above this would resolve to public_activity/_default.html.(|erb|haml|slim|something_else).

If a view file does not exist then ActionView::MisingTemplate will be raised. If you wish to fallback to the old behaviour and use an i18n based translation in this situation you can specify a :fallback parameter of text to fallback to this mechanism like such:

<%= render_activity(@activity, fallback: :text) %>

i18n

Translations are used by the #text method, to which you can pass additional options in form of a hash. #render method uses translations when view templates have not been provided. You can render pure i18n strings by passing {display: :i18n} to #render_activity or #render.

Translations should be put in your locale .yml files. To render pure strings from I18n Example structure:

activity:
  article:
    create: 'Article has been created'
    update: 'Someone has edited the article'
    destroy: 'Some user removed an article!'

This structure is valid for activities with keys "activity.article.create" or "article.create". As mentioned before, "activity." part of the key is optional.

Testing

For RSpec you can first disable public_activity and add require helper methods in the rails_helper.rb with:

#rails_helper.rb
require 'public_activity/testing'

PublicActivity.enabled = false

In your specs you can then blockwise decide whether to turn public_activity on or off.

# file_spec.rb
PublicActivity.with_tracking do
  # your test code goes here
end

PublicActivity.without_tracking do
  # your test code goes here
end

Documentation

For more documentation go here

Common examples

Set the Activity's owner to current_user by default

You can set up a default value for :owner by doing this:

  1. Include PublicActivity::StoreController in your ApplicationController like this:
class ApplicationController < ActionController::Base
  include PublicActivity::StoreController
end
  1. Use Proc in :owner attribute for tracked class method in your desired model. For example:
class Article < ActiveRecord::Base
  tracked owner: Proc.new{ |controller, model| controller.current_user }
end

Note: current_user applies to Devise, if you are using a different authentication gem or your own code, change the current_user to a method you use.

Disable tracking for a class or globally

If you need to disable tracking temporarily, for example in tests or db/seeds.rb then you can use PublicActivity.enabled= attribute like below:

# Disable p_a globally
PublicActivity.enabled = false

# Perform some operations that would normally be tracked by p_a:
Article.create(title: 'New article')

# Switch it back on
PublicActivity.enabled = true

You can also disable public_activity for a specific class:

# Disable p_a for Article class
Article.public_activity_off

# p_a will not do anything here:
@article = Article.create(title: 'New article')

# But will be enabled for other classes:
# (creation of the comment will be recorded if you are tracking the Comment class)
@article.comments.create(body: 'some comment!')

# Enable it again for Article:
Article.public_activity_on

Create custom activities

Besides standard, automatic activities created on CRUD actions on your model (deactivatable), you can post your own activities that can be triggered without modifying the tracked model. There are a few ways to do this, as PublicActivity gives three tiers of options to be set.

Instant options

Because every activity needs a key (otherwise: NoKeyProvided is raised), the shortest and minimal way to post an activity is:

@user.create_activity :mood_changed
# the key of the action will be user.mood_changed
@user.create_activity action: :mood_changed # this is exactly the same as above

Besides assigning your key (which is obvious from the code), it will take global options from User class (given in #tracked method during class definition) and overwrite them with instance options (set on @user by #activity method). You can read more about options and how PublicActivity inherits them for you here.

Note the action parameter builds the key like this: "#{model_name}.#{action}". You can read further on options for #create_activity here.

To provide more options, you can do:

@user.create_activity action: 'poke', parameters: {reason: 'bored'}, recipient: @friend, owner: current_user

In this example, we have provided all the things we could for a standard Activity.

Use custom fields on Activity

Besides the few fields that every Activity has (key, owner, recipient, trackable, parameters), you can also set custom fields. This could be very beneficial, as parameters are a serialized hash, which cannot be queried easily from the database. That being said, use custom fields when you know that you will set them very often and search by them (don't forget database indexes :) ).

Set owner and recipient based on associations

class Comment < ActiveRecord::Base
  include PublicActivity::Model
  tracked owner: :commenter, recipient: :commentee

  belongs_to :commenter, :class_name => "User"
  belongs_to :commentee, :class_name => "User"
end

Resolve parameters from a Symbol or Proc

class Post < ActiveRecord::Base
  include PublicActivity::Model
  tracked only: [:update], parameters: :tracked_values
  
  def tracked_values
   {}.tap do |hash|
     hash[:tags] = tags if tags_changed?
   end
  end
end

Setup

Skip this step if you are using ActiveRecord in Rails 4 or Mongoid

The first step is similar in every ORM available (except mongoid):

PublicActivity::Activity.class_eval do
  attr_accessible :custom_field
end

place this code under config/initializers/public_activity.rb, you have to create it first.

To be able to assign to that field, we need to move it to the mass assignment sanitizer's whitelist.

Migration

If you're using ActiveRecord, you will also need to provide a migration to add the actual field to the Activity. Taken from our tests:

class AddCustomFieldToActivities < ActiveRecord::Migration
  def change
    change_table :activities do |t|
      t.string :custom_field
    end
  end
end

Assigning custom fields

Assigning is done by the same methods that you use for normal parameters: #tracked, #create_activity. You can just pass the name of your custom variable and assign its value. Even better, you can pass it to #tracked to tell us how to harvest your data for custom fields so we can do that for you.

class Article < ActiveRecord::Base
  include PublicActivity::Model
  tracked custom_field: proc {|controller, model| controller.some_helper }
end

Help

If you need help with using public_activity please visit our discussion group and ask a question there:

https://groups.google.com/forum/?fromgroups#!forum/public-activity

Please do not ask general questions in the Github Issues.


Author: public-activity
Source code: https://github.com/public-activity/public_activity
License: MIT license

#ruby  #ruby-on-rails 

Jones Brianna

Jones Brianna

1602133570

Why You Should Design Your Own Board Game App?

https://www.mobiwebtech.com/why-you-should-design-your-own-board-game-app/

The dice, decks and game board are built with pixel art and you can design any game imaginable with your creativity. You can design a brand-new board game app or innovate your favourite game. You can design crazy games or role-playing maps with your art. You can play it online with your family and friends in your private game room in real-time.

#board game app development #board game app development company #board game app developers #board game app development services #board game app development usa #board game website development

Chess Board Boxes To Ensure Optimum Security To Your Chess Sets

Playing any game is not always sufficient enough, but paying complete attention is the thing that will strengthen your bond with the game. The same concept gets applied with the chess game too. There are lots of individuals available who take part in these chess games as their favorite pass time and also come with those results that are sufficient enough to drag them towards lots of satisfaction. Chess is just a game that requires lots of effort and your mental ability to conquer the win. However, these games are accessible in various locations like websites and others; the best way to enjoy these games is to play them from your home.

Shop the best chess sets available

The craze of various chess sets is becoming a revolutionary phenomenon where anybody can take part in these games without even facing any further hazards. Various websites are involved today in offering chess board boxes and other related essentials that can help you to have lots of fun and enjoyment with the game without even losing anything. Whether it is to take part in these games or to keep them safe, all you can do with the help of various websites engaged in offering lots of products to suit these game needs.

Protect your chess sets

These chess sets are durable enough due to being made from fine products, but still, they need lots of care and security to become functional for a long time. Just because, you have made lots of investments, hence you must protect them from wear and tear. To give them a safe place, you can also pick a chessboard with drawer that will offer you efficient handling of these chess sets without even creating any further hurdles. You can keep all the sets out during game play and can put it in the safe zone again once you have all done with the game.

#chess sets #chess sets online #chessboard with drawer #chess board boxes

The Best Way to Build a Chatbot in 2021

A useful tool several businesses implement for answering questions that potential customers may have is a chatbot. Many programming languages give web designers several ways on how to make a chatbot for their websites. They are capable of answering basic questions for visitors and offer innovation for businesses.

With the help of programming languages, it is possible to create a chatbot from the ground up to satisfy someone’s needs.

Plan Out the Chatbot’s Purpose

Before building a chatbot, it is ideal for web designers to determine how it will function on a website. Several chatbot duties center around fulfilling customers’ needs and questions or compiling and optimizing data via transactions.

Some benefits of implementing chatbots include:

  • Generating leads for marketing products and services
  • Improve work capacity when employees cannot answer questions or during non-business hours
  • Reducing errors while providing accurate information to customers or visitors
  • Meeting customer demands through instant communication
  • Alerting customers about their online transactions

Some programmers may choose to design a chatbox to function through predefined answers based on the questions customers may input or function by adapting and learning via human input.

#chatbots #latest news #the best way to build a chatbot in 2021 #build #build a chatbot #best way to build a chatbot

Riyad Amin

Riyad Amin

1571046022

Build Your Own Cryptocurrency Blockchain in Python

Cryptocurrency is a decentralized digital currency that uses encryption techniques to regulate the generation of currency units and to verify the transfer of funds. Anonymity, decentralization, and security are among its main features. Cryptocurrency is not regulated or tracked by any centralized authority, government, or bank.

Blockchain, a decentralized peer-to-peer (P2P) network, which is comprised of data blocks, is an integral part of cryptocurrency. These blocks chronologically store information about transactions and adhere to a protocol for inter-node communication and validating new blocks. The data recorded in blocks cannot be altered without the alteration of all subsequent blocks.

In this article, we are going to explain how you can create a simple blockchain using the Python programming language.

Here is the basic blueprint of the Python class we’ll use for creating the blockchain:

class Block(object):
    def __init__():
        pass
    #initial structure of the block class 
    def compute_hash():
        pass
    #producing the cryptographic hash of each block 
  class BlockChain(object):
    def __init__(self):
    #building the chain
    def build_genesis(self):
        pass
    #creating the initial block
    def build_block(self, proof_number, previous_hash):
        pass
    #builds new block and adds to the chain
   @staticmethod
    def confirm_validity(block, previous_block):
        pass
    #checks whether the blockchain is valid
    def get_data(self, sender, receiver, amount):
        pass
    # declares data of transactions
    @staticmethod
    def proof_of_work(last_proof):
        pass
    #adds to the security of the blockchain
    @property
    def latest_block(self):
        pass
    #returns the last block in the chain

Now, let’s explain how the blockchain class works.

Initial Structure of the Block Class

Here is the code for our initial block class:

import hashlib
import time
class Block(object):
    def __init__(self, index, proof_number, previous_hash, data, timestamp=None):
        self.index = index
        self.proof_number = proof_number
        self.previous_hash = previous_hash
        self.data = data
        self.timestamp = timestamp or time.time()
    @property
    def compute_hash(self):
        string_block = "{}{}{}{}{}".format(self.index, self.proof_number, self.previous_hash, self.data, self.timestamp)
        return hashlib.sha256(string_block.encode()).hexdigest()

As you can see above, the class constructor or initiation method ( init()) above takes the following parameters:

self — just like any other Python class, this parameter is used to refer to the class itself. Any variable associated with the class can be accessed using it.

index — it’s used to track the position of a block within the blockchain.

previous_hash — it used to reference the hash of the previous block within the blockchain.

data—it gives details of the transactions done, for example, the amount bought.

timestamp—it inserts a timestamp for all the transactions performed.

The second method in the class, compute_hash , is used to produce the cryptographic hash of each block based on the above values.

As you can see, we imported the SHA-256 algorithm into the cryptocurrency blockchain project to help in getting the hashes of the blocks.

Once the values have been placed inside the hashing module, the algorithm will return a 256-bit string denoting the contents of the block.

So, this is what gives the blockchain immutability. Since each block will be represented by a hash, which will be computed from the hash of the previous block, corrupting any block in the chain will make the other blocks have invalid hashes, resulting in breakage of the whole blockchain network.

Building the Chain

The whole concept of a blockchain is based on the fact that the blocks are “chained” to each other. Now, we’ll create a blockchain class that will play the critical role of managing the entire chain.

It will keep the transactions data and include other helper methods for completing various roles, such as adding new blocks.

Let’s talk about the helper methods.

Adding the Constructor Method

Here is the code:

class BlockChain(object):
    def __init__(self):
        self.chain = []
        self.current_data = []
        self.nodes = set()
        self.build_genesis()

The init() constructor method is what instantiates the blockchain.

Here are the roles of its attributes:

self.chain — this variable stores all the blocks.

self.current_data — this variable stores information about the transactions in the block.

self.build_genesis() — this method is used to create the initial block in the chain.

Building the Genesis Block

The build_genesis() method is used for creating the initial block in the chain, that is, a block without any predecessors. The genesis block is what represents the beginning of the blockchain.

To create it, we’ll call the build_block() method and give it some default values. The parameters proof_number and previous_hash are both given a value of zero, though you can give them any value you desire.

Here is the code:

def build_genesis(self):
        self.build_block(proof_number=0, previous_hash=0)
 def build_block(self, proof_number, previous_hash):
        block = Block(
            index=len(self.chain),
            proof_number=proof_number,
            previous_hash=previous_hash,
            data=self.current_data
        )
        self.current_data = []  
        self.chain.append(block)
        return block

Confirming Validity of the Blockchain

The confirm_validity method is critical in examining the integrity of the blockchain and making sure inconsistencies are lacking.

As explained earlier, hashes are pivotal for realizing the security of the cryptocurrency blockchain, because any slight alteration in an object will result in the creation of an entirely different hash.

Thus, the confirm_validity method utilizes a series of if statements to assess whether the hash of each block has been compromised.

Furthermore, it also compares the hash values of every two successive blocks to identify any anomalies. If the chain is working properly, it returns true; otherwise, it returns false.

Here is the code:

def confirm_validity(block, previous_block):
        if previous_block.index + 1 != block.index:
            return False
        elif previous_block.compute_hash != block.previous_hash:
            return False
        elif block.timestamp <= previous_block.timestamp:
            return False
        return True

Declaring Data of Transactions

The get_data method is important in declaring the data of transactions on a block. This method takes three parameters (sender’s information, receiver’s information, and amount) and adds the transaction data to the self.current_data list.

Here is the code:

def get_data(self, sender, receiver, amount):
        self.current_data.append({
            'sender': sender,
            'receiver': receiver,
            'amount': amount
        })
        return True

Effecting the Proof of Work

In blockchain technology, Proof of Work (PoW) refers to the complexity involved in mining or generating new blocks on the blockchain.

For example, the PoW can be implemented by identifying a number that solves a problem whenever a user completes some computing work. Anyone on the blockchain network should find the number complex to identify but easy to verify — this is the main concept of PoW.

This way, it discourages spamming and compromising the integrity of the network.

In this article, we’ll illustrate how to include a Proof of Work algorithm in a blockchain cryptocurrency project.

Finalizing With the Last Block

Finally, the latest_block() helper method is used for retrieving the last block on the network, which is actually the current block.

Here is the code:

def latest_block(self):
        return self.chain[-1]

Implementing Blockchain Mining

Now, this is the most exciting section!

Initially, the transactions are kept in a list of unverified transactions. Mining refers to the process of placing the unverified transactions in a block and solving the PoW problem. It can be referred to as the computing work involved in verifying the transactions.

If everything has been figured out correctly, a block is created or mined and joined together with the others in the blockchain. If users have successfully mined a block, they are often rewarded for using their computing resources to solve the PoW problem.

Here is the mining method in this simple cryptocurrency blockchain project:

def block_mining(self, details_miner):
            self.get_data(
            sender="0", #it implies that this node has created a new block
            receiver=details_miner,
            quantity=1, #creating a new block (or identifying the proof number) is awarded with 1
        )
        last_block = self.latest_block
        last_proof_number = last_block.proof_number
        proof_number = self.proof_of_work(last_proof_number)
        last_hash = last_block.compute_hash
        block = self.build_block(proof_number, last_hash)
        return vars(block)

Summary

Here is the whole code for our crypto blockchain class in Python:

import hashlib
import time
class Block(object):
    def __init__(self, index, proof_number, previous_hash, data, timestamp=None):
        self.index = index
        self.proof_number = proof_number
        self.previous_hash = previous_hash
        self.data = data
        self.timestamp = timestamp or time.time()
    @property
    def compute_hash(self):
        string_block = "{}{}{}{}{}".format(self.index, self.proof_number, self.previous_hash, self.data, self.timestamp)
        return hashlib.sha256(string_block.encode()).hexdigest()
    def __repr__(self):
        return "{} - {} - {} - {} - {}".format(self.index, self.proof_number, self.previous_hash, self.data, self.timestamp)
class BlockChain(object):
    def __init__(self):
        self.chain = []
        self.current_data = []
        self.nodes = set()
        self.build_genesis()
    def build_genesis(self):
        self.build_block(proof_number=0, previous_hash=0)
    def build_block(self, proof_number, previous_hash):
        block = Block(
            index=len(self.chain),
            proof_number=proof_number,
            previous_hash=previous_hash,
            data=self.current_data
        )
        self.current_data = []  
        self.chain.append(block)
        return block
    @staticmethod
    def confirm_validity(block, previous_block):
        if previous_block.index + 1 != block.index:
            return False
        elif previous_block.compute_hash != block.previous_hash:
            return False
        elif block.timestamp <= previous_block.timestamp:
            return False
        return True
    def get_data(self, sender, receiver, amount):
        self.current_data.append({
            'sender': sender,
            'receiver': receiver,
            'amount': amount
        })
        return True        
    @staticmethod
    def proof_of_work(last_proof):
        pass
    @property
    def latest_block(self):
        return self.chain[-1]
    def chain_validity(self):
        pass        
    def block_mining(self, details_miner):       
        self.get_data(
            sender="0", #it implies that this node has created a new block
            receiver=details_miner,
            quantity=1, #creating a new block (or identifying the proof number) is awared with 1
        )
        last_block = self.latest_block
        last_proof_number = last_block.proof_number
        proof_number = self.proof_of_work(last_proof_number)
        last_hash = last_block.compute_hash
        block = self.build_block(proof_number, last_hash)
        return vars(block)  
    def create_node(self, address):
        self.nodes.add(address)
        return True
    @staticmethod
    def get_block_object(block_data):        
        return Block(
            block_data['index'],
            block_data['proof_number'],
            block_data['previous_hash'],
            block_data['data'],
            timestamp=block_data['timestamp']
        )
blockchain = BlockChain()
print("GET READY MINING ABOUT TO START")
print(blockchain.chain)
last_block = blockchain.latest_block
last_proof_number = last_block.proof_number
proof_number = blockchain.proof_of_work(last_proof_number)
blockchain.get_data(
    sender="0", #this means that this node has constructed another block
    receiver="LiveEdu.tv", 
    amount=1, #building a new block (or figuring out the proof number) is awarded with 1
)
last_hash = last_block.compute_hash
block = blockchain.build_block(proof_number, last_hash)
print("WOW, MINING HAS BEEN SUCCESSFUL!")
print(blockchain.chain)

Now, let’s try to run our code to see if we can generate some digital coins…

Wow, it worked!

Conclusion

That is it!

We hope that this article has assisted you to understand the underlying technology that powers cryptocurrencies such as Bitcoin and Ethereum.

We just illustrated the basic ideas for making your feet wet in the innovative blockchain technology. The project above can still be enhanced by incorporating other features to make it more useful and robust.

Learn More

Thanks for reading !

Do you have any comments or questions? Please share them below.

#python #cryptocurrency