0. StarCraft II Python Bot: Introduction

# Python library for StarCraft II

«An easy-to-use library for writing AI Bots for StarCraft II in Python 3. The ultimate goal is simplicity and ease of use, while still preserving all functionality. A really simple worker rush bot should be no more than twenty lines of code, not two hundred. However, this library intends to provide both high and low level abstractions.»


You can find how to install in the previous link.

# About this tutorial

I structured this tutorial in three parts. From the beginning to the end it adds more and more things your bot can do. Every chapter will add a new functionality and when you finish your (Terran) bot, it will have a basic behaviour: gather, build an army, attack.

This tutorial has two parts to get you started on programming a StarCraft II Bot using python libraries and the StarCraft II game.

The structure is the next:
  •     0. This introduction
  •     1. Gathering resources
  •     2. Building units

# Starcraft Race of the bot

- Terran

The terrans (or humans) are a young species with psionic potential. The terrans of the Koprulu sector descend from the survivors of a disastrous 23rd century colonization mission from Earth.



1. StarCraft II Python Bot: Gathering resources

 # Command Center

The Command Center is the main building you need. On it you build your workers. You start with one the match and can expand your territory with it.

# Building the workers

async def buildWorkers(self):
for commandcenter in self.units(UnitTypeId.COMMANDCENTER).ready.noqueue:
if self.can_afford(UnitTypeId.SCV) and self.workers.amount < self.units(UnitTypeId.COMMANDCENTER).amount * 14 + 4:
await self.do(commandcenter.train(UnitTypeId.SCV))
This is simply a function to tell the Terran Command Center to create SCVs. We need to use it in the class, so let’s put it in the class and call it in the on_sept() function. The final script of this part should look like this:
import sc2
from sc2 import run_game, maps, Race, Difficulty
from sc2.player import Bot, Computer
from sc2.ids.unit_typeid import *
class AlanBot(sc2.BotAI):
async def on_step(self, iteration):
await self.buildWorkers()
async def buildWorkers(self):
for commandcenter in self.units(UnitTypeId.COMMANDCENTER).ready.noqueue:
if self.can_afford(UnitTypeId.SCV) and self.workers.amount < self.units(UnitTypeId.COMMANDCENTER).amount * 14 + 4:
await self.do(commandcenter.train(UnitTypeId.SCV))
run_game(maps.get("AbyssalReefLE"), [
Bot(Race.Terran, AlanBot()),
Computer(Race.Zerg, Difficulty.Hard)
], realtime=True)

# Gatering minerals

The developers of this library have already implemented a function to distribute_workers(), it will gather the minerals and Vespene Gas, but it won’t create create the Vespene Refinery (in Terran case). We need to create the Refineries too.
import sc2
from sc2 import run_game, maps, Race, Difficulty
from sc2.player import Bot, Computer
from sc2.ids.unit_typeid import *
class AlanBot(sc2.BotAI):
async def on_step(self, iteration):
await self.buildWorkers()
await self.distribute_workers()
async def buildWorkers(self):
for commandcenter in self.units(UnitTypeId.COMMANDCENTER).ready.noqueue:
if self.can_afford(UnitTypeId.SCV) and self.workers.amount < self.units(UnitTypeId.COMMANDCENTER).amount * 14 + 4:
await self.do(commandcenter.train(UnitTypeId.SCV))
run_game(maps.get("AbyssalReefLE"), [
Bot(Race.Terran, AlanBot()),
Computer(Race.Zerg, Difficulty.Hard)
], realtime=True)

# Gathering vesper gas

In order to collect the vespene gas, we need refineries (or Extractors or Assimlators for other Races) for the Terran SVCs.
async def buildRefineries(self):
for commandcenter in self.units(UnitTypeId.COMMANDCENTER).ready:
vespenes = self.state.vespene_geyser.closer_than(18.0, commandcenter)
for vespene in vespenes:
if not self.can_afford(UnitTypeId.REFINERY):
break
worker = self.select_build_worker(vespene.position)
if worker is None:
break
if not self.units(UnitTypeId.REFINERY).closer_than(1.0, vespene).exists:
await self.do(worker.build(UnitTypeId.REFINERY, vespene))

Write this as a class function of our bot and call it on the on_step() function:
import sc2
from sc2 import run_game, maps, Race, Difficulty
from sc2.player import Bot, Computer
from sc2.ids.unit_typeid import *
class AlanBot(sc2.BotAI):
async def on_step(self, iteration):
await self.buildWorkers()
await self.distribute_workers()
await self.buildRefineries()
async def buildWorkers(self):
for commandcenter in self.units(UnitTypeId.COMMANDCENTER).ready.noqueue:
if self.can_afford(UnitTypeId.SCV) and self.workers.amount < self.units(UnitTypeId.COMMANDCENTER).amount * 14 + 4:
await self.do(commandcenter.train(UnitTypeId.SCV))
async def buildRefineries(self):
for commandcenter in self.units(UnitTypeId.COMMANDCENTER).ready:
vespenes = self.state.vespene_geyser.closer_than(18.0, commandcenter)
for vespene in vespenes:
if not self.can_afford(UnitTypeId.REFINERY):
break
worker = self.select_build_worker(vespene.position)
if worker is None:
break
if not self.units(UnitTypeId.REFINERY).closer_than(1.0, vespene).exists:
await self.do(worker.build(UnitTypeId.REFINERY, vespene))
run_game(maps.get("AbyssalReefLE"), [
Bot(Race.Terran, AlanBot()),
Computer(Race.Zerg, Difficulty.Hard)
], realtime=True)

You should now have workers being created and gathering minerals and vespene gas for you.

2. StarCraft II Python Bot: Building units

 You need to check different things. When you want to create one Barrack you need:

And when you want to create a Marine you need:

  • Enough minerals
  • At least one Barrack
  • A limit of units to create

This is why we create two different functions: buildStructure() and buildOffensiveUnit().

- buildStructure()

In this function we are going to receive two parameters: the structure that we want to build, and the amount of structures that we want of it.

A good place to start building some Supply Depots and Barracks is around your first Command Center. So we are going to save the position in a variable:
commandCenter = self.units(UnitTypeId.COMMANDCENTER).ready.random
nearCC = await self.find_placement(UnitTypeId.SUPPLYDEPOT, commandcenter.position, placement_step=2)
But for the Barracks is a little more special: we need to check if there are already Supply Depots (see the Terran Tech Tree), if we have reached the limit of Barracks that we want, and if we can afford to built it.

The complete function is the following:
async def buildStructure(self, structureName, amount):
if self.units(UnitTypeId.COMMANDCENTER).ready.exists:
commandCenter = self.units(UnitTypeId.COMMANDCENTER).ready.random
workers = self.workers.gathering
nearCC = await self.find_placement(UnitTypeId.SUPPLYDEPOT, commandCenter.position, placement_step=2)
if structureName == 'supplydepot':
if self.supply_left < 6 and self.can_afford(UnitTypeId.SUPPLYDEPOT):
if workers:
w = workers.furthest_to(workers.center)
if nearCC:
await self.do(w.build(UnitTypeId.SUPPLYDEPOT, nearCC))
if structureName == 'barracks':
if self.units.of_type([UnitTypeId.SUPPLYDEPOT, UnitTypeId.SUPPLYDEPOTLOWERED, UnitTypeId.SUPPLYDEPOTDROP]).ready.exists
and self.units(UnitTypeId.BARRACKS).amount + self.already_pending(UnitTypeId.BARRACKS) < amount
and self.can_afford(UnitTypeId.BARRACKS):
if workers:
w = workers.furthest_to(workers.center)
if nearCC:
await self.do(w.build(UnitTypeId.BARRACKS, nearCC))
The nearCC variable store a location that match a free place for the Supply Depots size. You can add a second location that find a place for the size of the Barracks later.

- buildOffensiveUnit()

The offensive unit is the way of the program to spawn our Terrans. We pass it a name, where to build it, and how many of them.
async def buildOffensiveUnit(self, unitName, structureName, maxAmount):
structure = self.unitSelector(structureName)
unit = self.unitSelector(unitName)
if self.units(structure).ready.exists:
for struct in self.units(structure).ready.noqueue:
if self.can_afford(unit) and self.supply_left > 0 and self.units(unit).amount < maxAmount:
await self.do(struct.train(unit))

- Call in all in on_step()

For the bot to do things you need to put all your functions in the on_step() function. That way the program takes control on the synchs, and will launch everything written in the function. Maybe a good thing would be making a FSM machine, but this case is very simple.

You can give it a bit of logic for some amounts of units.
async def on_step(self, iteration):
await self.buildWorkers()
await self.distribute_workers()
await self.buildRefineries()
await self.buildStructure('supplydepot', 2000)
await self.buildStructure('barracks', 4)
await self.buildOffensiveUnit('marine', 'barracks', 27)
if self.units(UnitTypeId.MARINE).amount > 17:
await self.move('marine', True)
else:
await self.move('marine', False)


Simplest terminal menu - Python

Here you can find a start up code for running your python project. 

Import the necessary code at the beginning and modify the options in the options dictionary.

import time

# Import here the main part of your code
from My_script import Script

def user_data():
options = {
0: "option 0",
1: "option 1",
2: "option 2"
}
# DISPLAY MENU WITH OPTIONS
while 1:
print("\nSelect and option")
for option in options.items():
print(option)
selectedOption = input("Option to use: ")
if selectedOption.isdigit():
selectedOption = int(selectedOption)
if selectedOption not in options:
print("Error, not a valid option")
else:
break
else:
print("Error, choose the option digit")

# READ A FILENAME
while 1:
file_name = input("Write the file name")
try:
with open(file_name) as f:
# File opened successfully
file = f
break
except FileNotFoundError:
print("Error, couldn't open the file")

return file, options[selectedOption]

# Beginning of the script
print("[ # PROGRAM_NAME # ]")
print("Init program...")
file, option = user_data()
start = time.time()
print("Starting the program...")
Script.main(file, option)
print("Total time:", round(time.time() - start, 3), "seconds.")


Unity - Oculus Quest 2 Development Setup

We are going to setup the environment and run a Demo application for the Oculus Quest 2 with Unity.


# First Step

You need to pair to your smartphone and enable Developer Mode in the Oculus app for the smartphone. Once you have done this you can continue in your computer by installing Unity.


# Unity installation

First install Unity Hub and download Unity (a LTS version if you want)

With the installation complete create a new Unity 3D project.

Now we have an empty 3D project:
If you didn't install the needed External Tools as the JDK and SDK, you need to install them from the Unity Hub, just Add Modules to the Unity install, add the Android SDK & NDK Tools, and if you don't have it either, install the Open JDK.
Now if you go to the Build Settings, switch to Android and, with your Oculus Quest connected via USB to your computer, make sure that you can see the device in Run Device.
The name of the device should be something like you see here:

# Oculus Tools 

We need to download the tools from the Unity Asset Store, they are free. Go to the Asset Store and add to Unity the Oculus Integration

In the Unity Package Manager, add the Oculus Integration by downloading and Import everything to your project.


# Unity Setup

There are some changes that we need to do in the Unity settings to get the games and the demos to work properly in the Oculus, such as use OpenGLES3 and the installation of the XR Plug-in.

- Using OpenGLES3

In order to Build and Run for the Oculus Quest 2 we need to change the Graphic API, we need to put the OpenGLES3 before Vulkan in the Project Settings/Player/Other Settings:

- Install XR Plug-in

Make sure that you have XR Plug-in installed for the Oculus. In the project Settings go to XR Plug-in Manager and install it. Then in the Android tab, check the Oculus:

# Testing the Demo Files

You can find some Demos in the files that we imported. Look under the Assets/Oculus/SampleFramework/Usage, there you can find the Demo files such as DistanceGrab:

- Run the demo

Now you can add the scene to the Scenes in Build in the Build Settings and click Build and Run, and make sure to allow your computer to write on the Oculus. Put the glasses on and accept the dialogs. Then the compilation should be starting and when it finishes you can see the demo in your Oculus.
 


And that's it! We have a running game created and compiled with Unity in the Oculus Quest 2.

Blender - Procedural Sand Shader - with Download

Quick sand material / shader to add to your project, with a NodeGroup to make it easy to change the parameters. Download at the bottom of the post.














Use Three.js inside Ruby on Rails

This example uses the online resources of three.js, we don't have to download any three.js packages.




# Create the Rails webpage


We are going to start with the normal Ruby on Rails new project in a terminal:

  • $ rails new app
  • $ cd app
Test if the app is running by:
If you see a welcome page saying "Yay! You're on Rails!" everything went correctly.


# Create a home page


By creating a new controller we can set it as the home page for this project:

  • $ rails g controller home index

Now we have  home controller with an action called index, which will be the home page once we setup the routes. Right now the only way of accessing the new view of this controller is going to http://localhost:3000/home/index. But with a new route we can set this as the landing page.

Go to the routes file in app/config/routes.rb, then write a new line to indicate the new route:

  • root 'home#index' 

And delete the other route. Then the routers.rb should be looking like this:

Rails.application.routes.draw do
root 'home#index'
end

The homepage is set. You can go to http://localhost:3000/ and find the home page as the landing page.



# Put a three.js canvas in the home page


In order to use three.js inside the page we can use a partial to have everything organised in our project. By doing a partial we can have a separate file to write our three.js code.

Create a partial


Go to the app/views/home folder and create a new file called:

  • _3d_viewer.html.erb

Is very important to use the '_' in the beginning of the name of a partial.

Now we can use this partial to write our three.js code. We just need to reference the library and use it with two script tag
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>

<script>
<!-- three.js code goes here -->
</script>

With the source as https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js we can use the three.js library.

Show the partial in home page


The only thing left to do is to show the three.js in the home page. Just write one more line in the view for the homepage:
<h1>Home#index</h1>
<p>Find me in app/views/home/index.html.erb</p>

<%= render partial: "3d_viewer" %>


# View the results


With an initial code for three.js to work we can see the results in the homepage:




Three.js code of the example


In the _3d_viewer.html.erb
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>

<script>
var scene = new THREE.Scene();
const WIDTH = 500;
const HEIGHT = 500;
var camera = new THREE.PerspectiveCamera( 75, WIDTH/HEIGHT, 0.1, 1000 );
camera.position.z = 4;
var renderer = new THREE.WebGLRenderer({antialias:true});

renderer.setClearColor("#000000");
renderer.setSize( 400, 400 );
document.body.appendChild( renderer.domElement );

var geometry = new THREE.BoxGeometry( 1, 1, 1 );
var material = new THREE.MeshBasicMaterial( { color: "#433F81" } );
var cube = new THREE.Mesh( geometry, material );

scene.add( cube );

var render = function () {
requestAnimationFrame( render );

cube.rotation.y += 0.01;

renderer.render(scene, camera);
};

render();
</script>


Terran Bot for StarCraft II - Python

Find the explanation in  https://xcran.blogspot.com/2022/03/0-starcraft-ii-python-bot-introduction.html

import sc2
from sc2 import run_game, maps, Race, Difficulty
from sc2.player import Bot, Computer
from sc2.ids.unit_typeid import *


class AlanBot(sc2.BotAI):
async def on_step(self, iteration):
await self.buildWorkers()
await self.distribute_workers()
await self.buildRefineries()
await self.buildStructure('supplydepot', 2000)
await self.buildStructure('barracks', 4)
await self.buildOffensiveUnit('marine', 'barracks', 27)
if self.units(UnitTypeId.MARINE).amount > 17:
await self.move('marine', True)
else:
await self.move('marine', False)

async def buildWorkers(self):
for commandcenter in self.units(UnitTypeId.COMMANDCENTER).ready.noqueue:
if self.can_afford(UnitTypeId.SCV) and self.workers.amount < self.units(UnitTypeId.COMMANDCENTER).amount * 14 + 4:
await self.do(commandcenter.train(UnitTypeId.SCV))

async def buildRefineries(self):
for commandcenter in self.units(UnitTypeId.COMMANDCENTER).ready:
vespenes = self.state.vespene_geyser.closer_than(
18.0, commandcenter)
for vespene in vespenes:
if not self.can_afford(UnitTypeId.REFINERY):
break
worker = self.select_build_worker(vespene.position)
if worker is None:
break
if not self.units(UnitTypeId.REFINERY).closer_than(1.0, vespene).exists:
await self.do(worker.build(UnitTypeId.REFINERY, vespene))

async def buildStructure(self, structureName, amount):
if self.units(UnitTypeId.COMMANDCENTER).ready.exists:
commandCenter = self.units(UnitTypeId.COMMANDCENTER).ready.random
workers = self.workers.gathering

nearCC = await self.find_placement(UnitTypeId.SUPPLYDEPOT, commandCenter.position, placement_step=2)

if structureName == 'supplydepot':
if self.supply_left < 6 and self.can_afford(UnitTypeId.SUPPLYDEPOT):
if workers:
w = workers.furthest_to(workers.center)
if nearCC:
await self.do(w.build(UnitTypeId.SUPPLYDEPOT, nearCC))

if structureName == 'barracks':
if self.units.of_type([UnitTypeId.SUPPLYDEPOT, UnitTypeId.SUPPLYDEPOTLOWERED, UnitTypeId.SUPPLYDEPOTDROP]).ready.exists \
and self.units(UnitTypeId.BARRACKS).amount + self.already_pending(UnitTypeId.BARRACKS) < amount \
and self.can_afford(UnitTypeId.BARRACKS):
if workers:
w = workers.furthest_to(workers.center)
if nearCC:
await self.do(w.build(UnitTypeId.BARRACKS, nearCC))

async def buildOffensiveUnit(self, unitName, structureName, maxAmount):
structure = self.unitSelector(structureName)
unit = self.unitSelector(unitName)
if self.units(structure).ready.exists:
for struct in self.units(structure).ready.noqueue:
if self.can_afford(unit) and self.supply_left > 0 and self.units(unit).amount < maxAmount:
await self.do(struct.train(unit))


run_game(maps.get("AbyssalReefLE"), [
Bot(Race.Terran, AlanBot()),
Computer(Race.Zerg, Difficulty.Medium)
], realtime=True)


Detener actividad de disco duro HDD desde terminal - GNU/Linux

Para detener la actividad del HDD de nuestro ordenador podemos hacer uso del comando hdparm, se usa como ejemplo el disco duro principal /dev/sda, probablemente quiera detener otro disco duro


Modo Sleep:
  • $ sudo hdparm -Y /dev/sda
Modo StandBy:
  • $ sudo hdparm -y /dev/sda


Instalar .run en Ubuntu [GNU/Linux] desde terminal

  1. Desde el terminal acceda a la carpeta con los comandos cd y cd ..
  2. Dar permisos de ejecución:
    • sudo chmod +x archivo.run
  3. Ejecutar el archivo:
    • $ ./archivo.run

C Funtioncs - Count chars, words and lines

#include <stdio.h>

#define IN 1
#define OUT 0

int main(){
    int c, nl, nw, nc, state;
    state = OUT;
    nl = nw = nc = 0;
    while((c = getchar()) != EOF){
        ++nc; /*Count chars*/
        if(c == '\n') ++nl; /*Count lines*/
        if(c == ' ' || c == '\n' || c == '\t') state = OUT;
        else if(state == OUT){
             state = IN;
             ++nw; /*Count words*/
        }
     }
     printf("[Lines: %d]\n[Words: %d]\n[Chars: %d]\n", nl, nw, nc);
     return 0;
}

C Functions - Multiplication Example

#include <stdio.h>

int multNumbers(int a, int b);

int main(){
    int number1 = 3;
    int number2 = 4;
    int mult;

    mult = multNumbers(number1, number2);

    printf("mult = %d",mult);
    return 0;
}

int multNumbers(int a,int b){
    return result a * b;
}