Sun Tzu und die Kunst ein Lexikon zu schreiben

Sun Tzu sagt, jede Schlacht ist geschlagen bevor sie begonnen hat. Im Beispiel von Wikipedia heißt das, dass man bevor einen Text überhaupt dort einstellt man wissen sollte, wie darauf die Gegenseite reagiert. Um das herauszufinden habe ich folgendes Python Script erstellt:

# -*- coding: utf-8 -*-
'''
Created on 22.02.2017
Wikipedia Check
@author: Manuel Rodriguez
'''
class Quality: 
  def __init__(self):
    self.sentences = input('How much sentences? ')
    self.sources = input('How much sources from Google Scholar? ')
    self.doi = input('... from that with a DOI number? ')
    self.orthography = input('Is orthography correct? (0=no, 1=yes) ')
    self.quotationmarks = input('Are the quotation marks changed from"that" to „that“? (0=no, 1=yes) ')
    self.result()
  def result(self):
    self.neededcitation = 5
    self.neededdoi = 0.5
    count1 = 1.0 * self.sources / self.sentences
    count2 = 1.0 * self.doi / self.sources
    cond1,cond2,cond3,cond4=False,False,False,False
    if count1 >= (1.0*1 / self.neededcitation): cond1 = True 
    if count2 >= (1.0*self.neededdoi): cond2 = True
    if self.orthography==1: cond3=True
    if self.quotationmarks==1: cond4=True
    print "\nResult: ",
    if cond1==True and cond2==True and cond3==True and cond4==True:
      print "Text is great, please upload. Sighting can take up to 40 days"
    else: 
      print "Textquality is bad, Don't upload. The problems are:"
      if cond1==False: print "- number of sources"
      if cond2==False: print "- number of DOI"
      if cond3==False: print "- orthography"
      if cond4==False: print "- quotation marks"
 
if __name__ == "__main__":
  myQuality = Quality()

Nachdem man das Programm mit „python check.py“ startet muss man auf der Komamndozeile einige Fragen beantworten: Anzahl der Sätze, Anzahl Quellen, Anzahl Quelllen mit DOI Nummer, Rechtschreibung korrekt? und Anführungszeichen korrekt? Dann wird überprüft ob die Wikipedia-Qualitätskriterien erfüllt sind und entweder erscheint dann, dass alles super ist oder eben nicht. Der Check bietet eine erste Möglichkeit grob einzuschätzen ob sich ein Upload überhaupt lohnt. Vielleicht mal ein kleines Beispiel.

Auf meiner Festplatte habe ich schon einen kleinen Wikipedia Artikel geschrieben über ein Thema was interessant ist, und wozu es noch keinen Artikel bei Wikipedia gibt. Wenn ich jetzt das Programm starte, gebe ich bei Anzahl Sätze wahrheitsgemäß „10“ ein, bei Anzahl Quellen „1“ bei Anzahl Quellen mit einer DOI Nummer „0“, bei Rechtschreibung und Anführungszeichen beidesmal eine „0“ für nicht eingehalten. Die Software rechnet dann ein wenig und es wird ausgegeben, dass die Textqualität zu niedrig ist. Jetzt könnte ich natürlich den Text trotzdem zu Wikipedia hochladen. Nur, die Wahrscheinlichkeit ist hoch, dass es Stress gibt. Entweder gibt es einen Löschantrag oder ein erfahrener Admin hat daran etwas auszusetzen. Insofern ist es besser auf den Upload zu verzichten. Das ist zwar schade aber nicht zu ändern.

Wer sich den Sourcecode näher anschaut wird relativ schnell bemerken was man eingeben muss, damit die Software den Text für perfekt hält: man muss alle 5 Sätze mindestens eine Quelle haben und es müssen 50% DOI Quellen sein. Bei den Fragen 4 und 5 muss man beidesmal „1“ eingeben. Und ja, darüber kann man den Check austricksen.

Mentifex trollen aber richtig

Wer oder was Mentifex ist, ist zumindest im englischsprachigen Internet weit bekannt. Mindforth hat sich zu einer Art von Meme entwickelt, was dem Umstand geschuldet ist, dass es zu diesem Thema unglaublich viele Blogeinträge gibt, die kaum jemand alle gelesen haben kann. Im Kern steht darin geschrieben, dass eine Super-Human-AI namens Mindforth Realität ist. Der genaue Ablauf war, dass im Jahr 2008 Mindforth entstand welches sich dann in 2012 zu einer technologischen Singularität weiterentwickelt hat, http://mind.sourceforge.net/m4thuser.html#1.8

Soweit der O-Ton aus dem Mindforth-Universum. Es stellt sich jetzt die Frage wie man als Außenstehender darauf reagiert und wie man eine Gegenposition bezieht. Kurz gesagt geht es um die Frage wie man Mindforth trollt und damit hinterfragt. Das ist erstaunlich simpel. Im Grunde ist die obige Aussage, dass es seit 2012 eine technologische Singularität gibt, ähnlich zu bewerten wie der UFO Absturz in Roswell: es ist eine unbewiesene Behauptung für die es keinerlei Belege gibt. Mag sein, dass es wahr ist, aber es ist eine Verschwörungstheorie. Der geiegnete Umgang damit lautet, dass man sie debunken kann und dann auf das sogenannte Backfiring wartet. Backfiring bedeutet, dass man zum Debunken jene Informationen zusammenträgt die unabsichtlich die ursprüngliche These untermauern und eine Art von paranoide Blase erzeugen in die man selbst hineingezogen wird. Das ist zugleiche jene Methode um auch Mentifex zu trollen. Man muss nichts anderes machen, als auf der Basis von wissenschaftlicher Quellen darlegen, dass 2012 auf keinen Fall das Jahr von Singularity war weil ja die Künstliche Intelligenz noch gar nicht soweit ist.

Im Detail kann man das folgendermaßen machen: Angenommen es gäbe eine technologische Singularität, dann wäre die Folge dass die Super-AI alles weiß und alles kann. Sie wäre also im Stande fliegende Autos zu bauen, sie könnte durch die Zeit reisen, sie könnte den Turing-Test bestehen und sie könnte bei einem Fußballspiel gegen Menschen gewinnen. Ein Blick in die Nachrichten zeigt jedoch, dass es keine fliegenden Autos gibt, und auch beim Robocup Turnier hat noch kein Roboter gegen Menschen gewonnen. Das mit den Zeitreisen ist ohnehin Unfug. Anders formuliert, die ursprüngliche These kann nicht stimmen.

Aber bleiben wir ein wenig sachlicher: eine Super-Human-AI würde als erstes die US-Regierung unterwandern so wie es Skynet auch gemacht hat. Auch dafür gibt es keinerlei Anhaltspunkte. Ebenfalls würde eine Super-Human-AI die Kontrolle über die Minuteman-Raketen übernehmen um damit Russland anzugreifen, damit die dann zurückschießen was ebenfalls nicht passiert ist. Auch großflächtige Stromausfälle, oder Störungen im CERN Forschungszentrum sind anders als in der Terminator Trilogie im richtigen Leben nicht aufgetreten. Anders gesagt, Singularity ist nichts weiter als eine Illusion. Ein derart mächtige Software und Künstliche Intelligenz gibt es nur in der Einbildung von Drehbuchautoren. Jetzt könnte man natürlich behaupten, dass es ja auch Super-Human-AI gibt, die sich dann aber versteckt, so dass es keiner sehen kann. Das also selbstverständlich alle Computersysteme davon infiziert sind, aber alles im Geheimen. Und das es selbstverständlich fliegende Autos gibt, die aber ebenfalls über ein Tarnschild verfügen, weil es sonst eine Massenpanik gäbe …

Aber ich möchte nicht behaupten, dass die Texte von Mentifex komplett sinnlos sind. Wenn man sie als Hypothese versteht die es zu wiederlegen gilt kann man durchaus etwas dabei lernen. Denn beim Debunking von abenteuerlichen Spekulationen gelangt man an seriöse Informationen, also an überprüfbare Fakten die man aus Google Scholar nimmt und die eine DOI Nummer besitzen und mit etwas Glück gelingt diese zu wertvollen Informationen zu aggregieren. Mentifex Beschreibungen selber sind keine Wissenschaft, dessen Debunking dagegen schon.

Machen wir es etwas konkreter: in der Bibel steht dass Gott den Menschen erschaffen hat. Das mag so richtig sein, die Aussage ist jedoch keine Wissenschaft sondern ist Aberglaube pur. Sie ist nicht wissenschaftlich zu verstehen. Wenn man hingegen sie wiederlegt und dafür dann Methoden der Naturwissenschaften einsetzt und beispielsweise die Evolitionstheorie ins Spiel bringt, die Gentechnik usw. und das alles ins Verhältnis setzt zur Bibel lässt sich durchaus etwas daraus lernen. Das heißt, wer die Bibel alleine leißt und sonst gar nichts, der ist ein Dummkopf, wer jedoch sie in einen wissenschaflichen Kontext rückt wird durchaus ernsthafte Wissenschaft betreiben.

Die Mentifex Texte sind zu verstehen als eine Art von Bibel. Sie ergeben für sich allein betachtet keinen Sinn. Vermutlich sind sie ähnlich wie das alte Testament reine Phantasie. Dennoch kann die Beschäftigung damit lohnenswert sein, man muss eben harte Fakten dagegenhalten.

MASSENPANIK
Spielen wir doch einmal den unwahrscheinlichen Fall durch, dass Skynet Wirklichkeit ist. Also eine Superintelligenz existiert, die über Super-Human-AI Fähigkeiten verfügt. Würde diese Superintelligenz zeigen was sie kann, wäre eine Massenpanik die logische Folge. Denn was würde passieren, wenn über Newyork ein echtes UFO auftauchen würde? Richtig, die Leute würden in Ohnmacht fallen, CNN würde mit der Kamera draufhalten und die Leute würden nicht wissen wie sie damit umgehen sollen.

Nur mal zum Vergleich: als Steve Jobs in 2007 das erste iPhone präsentiert hat, sind die Leute schon ausgeflippt. Und das bei einem Gerät was über 128 MB RAM verfügte und eine grottenschlechte Auflösung. Nehmen wir mal an, jemand zaubert richtige Technologie aus dem Zylinder, es gäbe gar keine kulturelle Praxis um damit umzugehen.

Anders gesagt, es ist gar nicht mal so abwegig, dass einerseits eine Superintelligenz existiert auf der anderen Seite diese sich aber verstecken muss. Das Grundproblem ist, dass Technologie den Menschen Angst macht. Und zwar allen Menschen. Fortschritt hat immer auch etwas bedrohliches, es ist etwas was man nicht sofort versteht. Selbst Naturwissenschaftler verbringe viele Jahre damit, ihre natürliche Scheu vor dem Neuen abzulegen und der Wissenschaft mutig gegenüberzutreten. Naturwissenschaftlich gebildete Menschen sind jedoch die große Ausnahme auf dem Planeten Erde. Nach der letzten Zählung gibt es weltweit rund 1 Million professionelle Wissenschaftler, und selbst diese sind nicht alles Experten. Die viele anderen Milliarden von Menschen würden bei einer Ufo Panik ihr Weltbild verlieren. Sie würden vermutlich weglaufen, laut schreien, anfangen zu beten oder ähnliches. Fortschritt ist etwas, was man den Menschen nur dosiert zukommen lassen darf. Mag sein, dass es durchaus technikaffine Hacker gibt, die ein Ufo über Newyork mit einem spöttischen „So what“ beantworten würden und erstmal die Passpapiere der Aliens sehen wollen, aber was macht man mit den anderen Leuten?

Nur mal zur Erinnerung: im Film Termanator III gibt es eine lustige Szene wo erstmal ein Psychiater anrückt, weil jemand einen Roboter gesehen hat, als seelilscher Beistand um die Erfahrung zu verarbeiten. Und der T-800 war noch nichtmal ein richtiger Außerirdischer, sondern es war nur ein biped Robot mit einem beschränkten Vokabular. Nehmen wir mal an, in Echt würde so ein Roboter herumlaufen. Die meisten Menschen würde das erlebte nicht verarbeiten können. Sie wären komplett überfordert mit soetwas.

Geany vs Eclipse

Bisher dachte ich, dass Eclipse in Verbindung mit pydev die optimale Entwicklungsumgebung darstellt. Die komplexe Installation musste man eben ertragen. Doch es gibt möglicherweise eine Alternative. Der Texteditor/IDE Geany ist einen Blick wert. Geany ist zunächst als Ersatz für Gedit gedacht, das heißt, man kann damit ganz normal eine Textdatei öffnen und speichern. Wer will kann aber auch eine python Datei erstellen, dort dann „Hello World“ reinschreiben und mit einen Klick auf die Menüleiste startet sie dann. Der Vorteil von Geany ist, dass es anders als Eclipse sehr viel leichter zu installieren ist und das die Bootup-Zeit besser ist. Es fehlt zwar eine Refactoring Funktion aber dafür gibt es Syntax-Highlighting, Autoformat mit etwas Nachrüsten sowie einen Class-Browser.

Wer will kann unter Geany sogar Make-Files verwalten um aus größere Projekte zu starten. Das ist aber kein Muss. Man kann auch einfach nur so eine Python, Java oder was auch immer Datei erzeugen und die dann ausführen. Am ehesten Vergleichen lässt sich Geany mit VIM. Es ist ein Allzweck-Texteditor der sich an Programmierer richtet und leicht zu bedienen ist.

Der eigentliche Clou von Geany ist, dass die Build-Optionen zum Kompilieren sich halbwegs leicht konfigurieren lassen. Wer schonmal mit Kdevelop oder Eclipse herumgespielt hat wird wissen, dass es gar nicht so simpel ist, dort ein Include für eine externe Library anzugeben. Im Regelfall muss man sich erst durch viele Menüs klicken und selbst dann klappt es nicht immer. Bei Geany hingegen gibt es ein simples Config-Menü. Dort trägt man manuell ein, dass die Befehlszeile „g++ -std=c++11 -pthread -lBox2D file.cpp“ ausgeführt wird und sonst gar nichts. Und tatsächlich, exakt das wird dann ausgeführt und man erhält die fertige Binärdatei: sehr simpel aber effektiv. Irgendwelche Path-Menüs wo man automatisch nach Librarys suchen lassen kann wie bei Eclipse gibt es nicht. Entweder man hat die Library in seinem System korrekt installiert und weiß wie der Command-Line Befehl lautet oder man hat Pech gehabt. Das macht die Sache um einiges übersichtlicher.

Zugegeben, Geany ist deutlich komplexer aufgebaut als Gedit. Es ist mehr als ein simpler Texteditor, infolge dessen gibt es relative viele Menüs wo man etwas einstellen kann. Offenbar ist das Programmieren einer IDE ein komplexes Unterfangen, man muss sich also in Geany zunächst einarbeiten bevor man damit loslegen kann. Der Unterschied zu anderen Umgebungen wie Netbeans oder Eclipse liegt darin, dass diese Lernkurve irgendwann abgeschlossen ist. Das heißt irgendwann hat man alle Menüs einmal angeklickt. Bei Eclipse hingegen kann man mehrere Jahre die IDE nutzen und wird immernoch alles sehr ungewohnt und neu finden.

Warum Zauberkunst heute nicht mehr gefragt ist

Die Geschichte der Zauberei ließt sich wie ein Krimi. Gezaubert wurde schon vor mehreren Tausend Jahren, und Magie gibt es in allen Kulturen. Ob der indische Seiltrick, die schwebende Jungfrau oder französische Kartenzauberei, immer geht es darum, das Publikum in eine Traumwelt zu entführen. Wer jedoch aufmerksam die Geschichte der Zauberkunst studiert wird bermerken dass sie ab ungefähr dem Jahr 1900 langweilig wurde. Das war zugleich auch die Hochzeit der Zauberei, als in Zaubertheater Berufsmagier in Abendgarderobe auftragen und am nächsten Tag in der Zeitung diskutiert wurden. Seitdem ist die Zauberkunst auf einem absteigenden Pfad. Woran liegt das? Natürlich an der Erfindung des Films. Wer heute in eine andere Welt entschwinden möchte, besucht keine Zaubergala mahr sondern geht ins Kino.

Von der gesellschaftlichen Rolle haben Kino und Zauberei sehr viel gemeinsam. In beiden Fällen ist Verschwiegenheit oberstes Gebot. So wird im Regelfall nicht verraten was in einem Film passiert bevor er offiziell startet und wer einen Film von der Leinwand abfotografiert kommt im Regelfall ins Gefängnis. Allein von den Zuschauerzahlen und vom gesellschaftlichen Impact ist heute der Film das was früher einmal die Zauberei war. Vor allem aber ist Kino Motor gesellschaftlichen Fortschritts. Zur Erinnerung: im 19. Jahrhundert waren es Zauberkünstler die bereits den elektrischen Strom in ihre Vorstellungen einbauten. Als viele Leute nochnichtmal wussten, was das überhaupt ist, haben sie bereits künstliche Blitze erzeugt. Diese Vorreiterrolle ist der Zauberei inzwischen verloren gegangen, aber ihr legitimer Nachfolger ist das jetzt das Kino. Genau genommen war auch Walt Disney ein Zauberkünstler, nur einer der sich modernster Technologie bedient hat. Walt Disney war streng genommen ein Appratezauberer; nahezu sein gesammtes Werk basiert auf der Nutzung von physikalischen Gesetzmäßigkeiten und wie die Zauberer vor ihm hat Disney die Welt beeinflusst. So war es Disney der das Mondprogramm eines Werner von Braun publik gemacht hat und in Disney Spielfilmen wurden die ersten technischen Innovationen präsentiert.

Tutorial für Python und Geany

Wer das Progrmamieren erlernen will hat es heute leichter denn je. Es gibt zahlreiche Programmiersprache und viele gute Texteditoren mit denen auch Neueinsteiger schnell eigene Programme entwickeln können. Im folgenden wurde Geany als IDE und Python als Programmiersprache ausgewählt um das berühmte Hello World Beispiel zu erläutern.

Nachdem man Geany gestartet hat, gibt man den folgenden Sourcecode ein und klickt auf Run und hat damit das erste Python Programm geschrieben und ausgeführt. Es gibt einen String auf dem Bildschirm aus.

geany1

Um die Sache ein wenig spannender zu machen wird jetzt das Programm um objektorientierte Elemente erweitert. Eine eigene Klasse wird angelegt, ein Konstruktor definiert und eine Methode aufgerufen.

geany2

Wiederum drückt man nach der Eingabe auf Run. Der Vorteil gegenüber anderen Sprachen wie C++ liegt darin, dass man relativ schnelle Resultate erzielt. In vielen Fällen gibt es überhaupt keine Compiler-Fehler und wenn doch sind es simple Tippfehler die man ohne weitere Recherche on-the-fly beheben kann. Links in der Navigationsleiste von Geany baut sich automatisch die Programm-Gliederung auf mit einer Übersicht über alle Methoden und Klassen. Auf diese Weise kann die Komplexität des Programms langsam erhöht werden.

geany3

Eine Besonderheit von Geany ist, dass dort für alle gängigen Programmiersprachen bereits voreingestellt ist wie die Compiler-Befehle lauten. Bei Python steht in der Dialogbox einfach drin, dass nach einem Klick auf „Execute“ folgendes aufgerufen wird: python „%f“. Also dasselbe was man auch auf der Command-Line eingeben würde. Man kann diese Aufrufparameter ändern und wieder zurücksetzen auf Standard. Speziell bei Python braucht man vor Ausführung eines Programms nochnichtmal eine Kompilierung anstoßen, sondern kann das Programm direkt starten. Bei anderen Sprachen wie Java muss man vorher noch auf „Build“ klicken um eine Bytecode Datei zu erzeugen.

Wie weiter im Blog?

1

Als Screenshot habe ich mal die aktuelle Abrufstatistik der letzten Zeit eingefügt. Wie man sieht sind die Zahlen niedrig. Pro Tag rund 25 Aufrufe mit einem kleinen Außreißer. Warum der da ist? Keine Ahnung. Nur mal zur Einordnung, solange die tägliche Abrufzahl kleiner 100 ist spricht von man einem sogenannten C-Blog, also eine reichweite die extrem niedrig ist, was niemand kennt und wo es nur selten zu Kommentaren kommt. Das hier ist ein typisches C-Blog.

Anders als früher ist mir das inzwischen jedoch egal. Denn erstens kann man da ohnehin nichts gegen unternehmen, weil das Google Ranking sich nicht beeinflussen lässt und zum Zweiten weil ich bei Wikipedia die Möglichkeit haben, extrem hohen Traffic zu erzeugen, vorausgesetzt mein Posting dort wird nicht von der Eingangskontrolle einkassiert. Beispielsweise darf ich mich jetzt stolz als Co-Autor für den Wikipedia Eintrag Roboter nennen. Mein Absatz dort hat im Schnitt 400 Abrufe am Tag. Und zwar jeden Tag. Gleichzeitig hat Wikipedia bei google das maximal mögliche Ranking und so ist es ein kleiner Trost, dass ich über diesen Umweg doch noch ein wenig Traffic generiere.

Ebenfalls relativ hoch ist der Traffic wie auch die Kommentarhäufigkeit im Usenet und bei Stackoverflow, so dass ich langsam den Verdacht habe, dass Blogs generell weniger Traffic haben als Foren und große Portale. Auf der anderen Seite ist mir das blog hier dennoch ans Herz gewachsen. Weil es hier keine Moderatoren gibt, sondern wirklich nur das erscheint, was ich einstelle.

Update:
Absoluter Lieblingsartikel der Leser dieses Blogs ist derzeit https://trollheaven.wordpress.com/2016/07/24/das-7d-hologramm/ Offenbar besteht hier ein großes Informationsbedürfnis. Es könnte auch damit zusammenhängen dass Google unter diesem Stichwort und bei der Sprachwahl Deutsch, den Text derzeit auf Platz 1 der Rangliste einordnet. Warum? Keine Ahnung. Andere Blog-Einträge hingegen erhalten nur wenig Aufmerksamkeit von Außen.

Update: Robot-Control-System

Das Robot-Control-System hat kleinere Verbesserungen erhalten. Zum einen gibt es jetzt einen zweiten Gripper und zum zweiten wurden mehr Motion Primitive verbaut. Wenn man das Programm startet drückt man auf die Taste 5 und sieht dann eine längere Animation ablaufen, einen sogenannten Physics Animation Controller. Dieser besteht aus mehreren Finite-States-Maschines, die als Behavior Tree ineinander verschachtelt sind und erstaunlich komplexe Aktionen ausführen. Für den Benutzer passiert wenig: es ist nur ein Pick&Place Task zu sehen, der zudem noch mehrere Pausen enthält.

Das zentrale Element ist die Behavior Engine, welche sich wie folgt gliedert:

main
  task1
     selectgrasp
     place
     move(0)
     selectgrasp
     place
     move(1)
     selectgrasp
     place

Die Motion Primitive „selectgrasp“, „place“ und „move“ sind noch weiter untergliedert bis am Ende dann Steueranweisungen für die Box2D Physik-Engine ausgeführt werden. Die Leistungsfähigkeit des Roboters bemisst sich nach den Lines of Code für die Behavior Engine, je umfangreicher sie ist, desto mehr kann der Roboter machen. Das Prinzip ist in der Robotik nichts neues: Marc Raibert hat damit experimentiert, die NaturalMotion Engine funktioniert so, Simbicon basiert darauf und bei der Darpa Robotics Challange 2015 wurden die Controller ebenfalls nach diesem Prinzip realisiert. Neu ist jedoch, dass diesmal Python als Programmiersprache verwendet wurde. Damit ist die Verständlichkeit hoffentlich höher, als bei ROS und vergleichbaren Projekten.

'''
Created on 16.02.2017
gantry robot
@author: Manuel Rodriguez
'''
import time, sys, pygame, random, math, Box2D, os, numpy, threading
import logging
from Box2D.b2 import (world, polygonShape, staticBody, dynamicBody)
from Box2D import (b2CircleShape, b2FixtureDef, b2LoopShape, b2PolygonShape,
                   b2RevoluteJointDef, b2_pi)
from locale import currency
from __builtin__ import True
logging.basicConfig(
  stream=sys.stdout,
  level=logging.DEBUG,
  format='%(message)s'
)
log = logging.getLogger(__name__)

class Settings(object):
  def __init__(self):
    self.screen = (600, 500)
    self.ppm = 32.0  # pixels per meter
    self.fric = 1.0  # 0.3 friction
    self.torque = 1000
    self.fps = 55  # 60 fps
    self.pause = 100000 / self.fps
    self.black = (0, 0, 0)
    self.grey = (150, 150, 150)
    self.red = (230, 0, 0)
    self.blue = (0, 0, 230)
  def box2d_to_pygame_pos(self, box2dpos):
    """ convert coordinates """
    x = int(box2dpos[0] * self.ppm)
    y = int(self.screen[1] - box2dpos[1] * self.ppm)
    return (x, y)
  def calcdistance(self, p1, p2):
    """ Euclidean ordinary distance """
    return math.sqrt((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2)
  def angle_between_two_points(self, p1, p2):
    angle = math.degrees(math.atan2(p2[1] - p1[1], p2[0] - p1[0]))
    angle += 90
    if angle < 0: angle += 360
    return angle
  def polarpoint(self, p1, angle, radius):
    """ polar coordinates for a point on circle """
    angle = (angle - 90) * math.pi / 180
    x = p1[0] + radius * math.cos(angle)
    y = p1[1] + radius * math.sin(angle) 
    x,y=self.floattoint((x,y))
    return (x, y)
  def floattoint(self, p):
    return (int(p[0]), int(p[1]))
  def incircle(self,p1,radius,p2):
    """ checks if p2 is in circle """
    square_dist = (p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2
    return square_dist <= radius ** 2
  def getpixel(self, p):
    surf3d = pygame.surfarray.pixels3d(self.window)
    temp=surf3d[p[0]][p[1]]
    del surf3d # prevent blocking
    return temp

class Physics(Settings):
  def __init__(self):
    super(Physics, self).__init__()
    self.myworld = world(gravity=(0, -10), doSleep=True)  # 0,-10
    self.groundbody = []
    self.body = []
    self.groundbody.append(self.myworld.CreateStaticBody(position=(15, 1),
        shapes=polygonShape(box=(20, 0.05)), angle=-0))  # ground
    self.groundbody.append(self.myworld.CreateStaticBody(position=(0.5, 10),
        shapes=polygonShape(box=(0.05, 10)),))  # left
    self.groundbody.append(self.myworld.CreateStaticBody(position=(18, 7),
        shapes=polygonShape(box=(0.05, 10)),))  # right
    self.groundbody.append(self.myworld.CreateStaticBody(position=(15, 13),
        shapes=polygonShape(box=(20, 0.05)),))  # top

  def getpos(self, id):
    box2dpos = self.body[id].position
    return self.box2d_to_pygame_pos(box2dpos)
  def getangle(self, id):
    angle = self.body[id].angle
    angle = (math.degrees(-angle) + 90) % 360
    return angle       
  def setspeed(self, id, speed):
    self.joint[id].motorSpeed = speed 
  def movelinear(self, id, p):
    self.body[id].linearVelocity = p
  def moveang(self, id, angle):
    self.body[id].angularVelocity = angle
  def updatePhysics(self):
    timestep = 1.0 * 3 / self.fps
    self.adjustgripper()
    self.myworld.Step(timestep, 1000, 10)

class Gripper(Physics):
  def __init__(self):
    super(Gripper, self).__init__()
    self.gripper1 = (0, 0)
    self.gripper2 = (550, 100)
    self.gripperangle1=180
    self.joint = []
    startid = len(self.body)
    log.info('add id single1 ' + str(startid))
    self.body.append(self.myworld.CreateDynamicBody(position=(10, 15), angle=0))
    self.body[-1].CreatePolygonFixture(box=(1, 0.5), density=1, friction=self.fric)
    self.body.append(self.myworld.CreateDynamicBody(position=(10, 10), angle=0))
    self.body[-1].CreatePolygonFixture(box=(0.5, 0.5), density=1, friction=self.fric)
    self.body.append(self.myworld.CreateDynamicBody(position=(9, 5), angle=0))
    self.body[-1].CreatePolygonFixture(box=(0.3, 1), density=1, friction=self.fric)
    self.body.append(self.myworld.CreateDynamicBody(position=(10, 5), angle=0))
    self.body[-1].CreatePolygonFixture(box=(0.3, 1), density=1, friction=self.fric)
    
    self.body.append(self.myworld.CreateDynamicBody(position=(9, 4), angle=0))
    self.body[-1].CreatePolygonFixture(box=(1, 1), density=1, friction=self.fric)

    self.joint.append(self.myworld.CreatePrismaticJoint(# x
        bodyA=self.body[startid],
        bodyB=self.groundbody[3],
        anchor=(0, 0),
        axis=(1, 0),
        lowerTranslation=-6.0,
        upperTranslation=6,
        enableLimit=False,
        maxMotorForce=self.torque,
        motorSpeed=0.0,
        enableMotor=True,
    ))     
    self.joint.append(self.myworld.CreatePrismaticJoint(# up/down
        bodyA=self.body[startid+1],
        bodyB=self.body[startid],
        anchor=(0, 0),
        axis=(0, 1),
        lowerTranslation=-15.0,
        upperTranslation=15,
        enableLimit=False,
        maxMotorForce=self.torque,
        motorSpeed=0.0,
        enableMotor=True,
    ))   
    self.joint.append(self.myworld.CreateRevoluteJoint(# rotate
        bodyA=self.body[startid+1],
        bodyB=self.body[startid+2],
        localAnchorA=(0, 0),
        localAnchorB=(0, 1),
        enableMotor=True,
        enableLimit=False,
        lowerAngle=math.radians(60),
        upperAngle=math.radians(120),
        maxMotorTorque=self.torque,
        motorSpeed=0,
    ))
    
    self.joint.append(self.myworld.CreatePrismaticJoint(# gripper openclose
        bodyA=self.body[startid+2],
        bodyB=self.body[startid + 3],
        localAnchorA=(0, 0),
        localAnchorB=(0, 0),
        axis=(1, 0),
        lowerTranslation=1,
        upperTranslation=3.4,
        enableLimit=True,
        maxMotorForce=self.torque,
        motorSpeed=1.0,
        enableMotor=True,
    ))    

    startid = len(self.body)
    log.info('add id single2 ' + str(startid))
    self.body.append(self.myworld.CreateDynamicBody(position=(10, 13.5), angle=0))
    self.body[-1].CreatePolygonFixture(box=(1, 0.5), density=1, friction=self.fric)
    self.body.append(self.myworld.CreateDynamicBody(position=(10, 10), angle=0))
    self.body[-1].CreatePolygonFixture(box=(0.4, 0.4), density=1, friction=self.fric)
    self.joint.append(self.myworld.CreatePrismaticJoint(# x
        bodyA=self.body[startid],
        bodyB=self.groundbody[3],
        anchor=(0, 0),
        axis=(1, 0),
        lowerTranslation=-6.0,
        upperTranslation=6,
        enableLimit=False,
        maxMotorForce=self.torque,
        motorSpeed=0.0,
        enableMotor=True,
    ))     
    self.joint.append(self.myworld.CreatePrismaticJoint(# up/down
        bodyA=self.body[startid+1],
        bodyB=self.body[startid],
        anchor=(0, 0),
        axis=(0, 1),
        lowerTranslation=-15.0,
        upperTranslation=15,
        enableLimit=False,
        maxMotorForce=self.torque,
        motorSpeed=0.0,
        enableMotor=True,
    ))   
    
  def adjustgripper(self):
    # position
    pos = self.gripper1
    p1 = self.getpos(1)
    dx = pos[0] - p1[0]
    dy = pos[1] - p1[1]
    dx = 2.0 * -dx /100  
    dy = 2.0 * dy / 100  
    self.setspeed(0, dx)
    self.setspeed(1, dy)
    # angle
    a1 = (self.getangle(2) + 90) % 360
    a2 = self.gripperangle1
    diff = a2 - a1
    da=-1.0*diff/100
    self.setspeed(2, da)

    # position gripper2
    pos = self.gripper2
    p1 = self.getpos(6)
    dx = pos[0] - p1[0]
    dy = pos[1] - p1[1]
    dx = 2.0 * -dx /100  
    dy = 2.0 * dy / 100  
    self.setspeed(4, dx)
    self.setspeed(5, dy)

class BehaviorTree(Gripper):
  def __init__(self):
    super(BehaviorTree, self).__init__()
    self.stopflag = False
  def BTstart(self):
    self.stopflag = False
    self.mythread = threading.Thread(target=self.taskmain)
    self.mythread.daemon = True  # stops, if mainprogram ends
    self.mythread.start()
  def BTstop(self):
    log.info('stop request')
    self.stopflag = True
  def taskmain(self):
    self.input = "auto"
    log.info('start thread')
    self.task1()
    self.input = "manuel 1" 
    log.info('end thread')
  def task1(self):
    self.taskselectgrasp()
    self.taskplace()
    self.taskmove(0)
    self.taskselectgrasp()
    self.taskplace()
    self.taskmove(1)
    self.taskselectgrasp()
    self.taskplace()
  def taskselectgrasp(self):
    type = self.grasptype()
    if type=="grasp": 
      self.taskgrasp()
      self.taskpostgrasp()
    if type=="tiltleft" or type=="tiltright":
      self.tasktilt(type)
      self.taskgrasp()
      self.taskpostgrasp()
  def taskpostgrasp(self):
    log.info('postgrasp')  
    # single rest
    p = self.getpos(6)
    self.taskmovexy(1, (p[0], 0))
    self.taskpause()
    self.taskmovexy(1, (550, 100))
    self.taskpause()
    # move up
    p = self.getpoint("grasp")
    self.taskmovexy(0, p[0])
    self.taskpause()
    self.taskrotate(180)
    self.taskpause()
  def taskplace(self):
    log.info('place ')
    p1=(250,200) # middle screen
    self.taskmovexy(0, p1)
    self.taskpause()
    self.taskopengripper()
    self.taskpause()
    p2=(0,0) # rest position
    self.taskmovexy(0, p2)
    self.taskpause()
  def taskmove(self,id):
    if id==0: 
      p1=(150,300)
      p2=(150,430)
      p3=(600,430)
      p4=(400,430)
    if id==1: 
      p1=(450,300)
      p2=(450,430)
      p3=(0,430)
      p4=(200,430)
    self.taskmovexy(1, p1)
    self.taskpause()
    self.taskmovexy(1, p2)
    self.taskpause()
    self.taskmovexy(1, p3)
    self.taskpause()
    self.taskmovexy(1, p4)
    self.taskpause()
    self.taskmovexy(1, (550, 100)) # rest
    self.taskpause()
  def taskgrasp(self):
    log.info('grasp ')
    self.taskrotate(180+self.angleobject())
    self.taskpause()
    p1,p2 = self.getpoint("grasp")[0],self.getpoint("grasp")[3]
    self.taskmovexy(0, p1)
    self.taskpause()
    self.taskmovexy(0, p2)
    self.taskpause()
    # close
    self.taskclosegripper()
    self.taskpause()
  def tasktilt(self,direction):
    log.info('tilt '+str(direction))
    # above
    p = self.getpoint(direction)
    self.taskmovexy(1, p[0])
    self.taskpause()
    # down
    self.taskmovexy(1, p[1])
    self.taskpause()
    # move
    self.taskmovexy(1, p[2])
    self.taskpause()

class Motionprimitive(BehaviorTree):
  def taskpause(self):
    pygame.time.wait(self.pause)
  def taskmovexy(self, id, p):
    log.info('-- movexy ' + str(id) + " " + str(p)) 
    if id == 0: self.gripper1 = p
    if id == 1: self.gripper2 = p 
  def taskopengripper(self):  
    log.info('-- opengripper')
    self.setspeed(3, 1)
  def taskclosegripper(self):
    log.info('-- closegripper')
    self.setspeed(3, -0.5)    
  def taskrotate(self, angle): 
    log.info('-- rotate '+str(angle))
    self.gripperangle1 = angle
        
class Cube(Motionprimitive):
  def __init__(self):
    super(Cube, self).__init__()
    self.objectid=4
  def angleobject(self):
    topangle = []
    for i in range(4):
      temp = (self.getangle(self.objectid) + i * 90) % 360  # angle for all directions
      # only top angles
      if temp < 90: diff = temp
      elif temp > 270: diff = temp - 360
      else: diff = 360
      topangle.append(diff)
    temp = sorted(topangle, key=abs)
    return temp[0]     
  def getpoint(self, action):
    p1 = self.getpos(self.objectid)
    angle = self.angleobject()
    pygame.draw.circle(self.window, self.red, self.floattoint(p1), 5, 2)
    if action == "grasp": 
      p2 = self.polarpoint(p1, angle - 90, 52)  # point left from center
      p3 = self.polarpoint(p2, angle, 100)
      p4 = self.polarpoint(p1, angle + 90, 52)  # point right from center
      p5 = self.polarpoint(p2, angle, 55) 
      pygame.draw.circle(self.window, self.red, self.floattoint(p2), 10, 2)
      pygame.draw.circle(self.window, self.red, self.floattoint(p3), 10, 2)
      pygame.draw.circle(self.window, self.red, self.floattoint(p4), 5, 2)
      pygame.draw.circle(self.window, self.red, self.floattoint(p5), 10, 2)
      return (p3, p2, p4,p5)
    if action == "tiltleft" or action == "tiltright":
      if action == "tiltleft": direction=-1
      if action == "tiltright": direction=1
      p2 = self.polarpoint(p1, angle + direction*90, 48)  # point left from center
      p7 = self.polarpoint(p1, angle + direction* 90, 10) 
      p8 = self.polarpoint(p7, angle, 45)
      p9 = self.polarpoint(p1, angle, 100)  # above center
      pygame.draw.circle(self.window, self.red, self.floattoint(p2), 10, 2)
      pygame.draw.circle(self.window, self.red, self.floattoint(p7), 5, 2)
      pygame.draw.circle(self.window, self.red, self.floattoint(p8), 10, 2)
      pygame.draw.circle(self.window, self.red, self.floattoint(p9), 5, 2)
      return (p9, p8, p2)
  def grasptype(self):
    log.info('check grasptype')
    arealeftfree, arearightfree, leftedge, rightedge = False, False, False, False
    # check grasp
    p = self.getpoint("grasp")
    colorleft, colorright = self.getpixel(p[1]), self.getpixel(p[2])
    #print p[1], colorleft
    if colorleft[0] == 220: arealeftfree = True
    if colorright[0] == 220: arearightfree = True
    # check border
    p = self.getpos(self.objectid)[0]
    if p < 100: leftedge = True
    if p > 510: rightedge = True
    # decision table
    type = None
    if leftedge == False and rightedge==False and arealeftfree == True and arearightfree == True: type= "grasp"
    elif arealeftfree == True and leftedge == False: type = "tiltleft"
    elif arearightfree == True and rightedge == False: type = "tiltright"
    return type
              
class GUI(Cube):
  def __init__(self):
    super(GUI, self).__init__()
    pygame.init()
    self.window = pygame.display.set_mode(self.screen)
    self.clock = pygame.time.Clock()
    self.mouse = (0, 0)
    self.input = "manuel 1"

  def paintobjects(self):
    # Text1
    text = str(self.input)
    text2 = " " + str(self.mouse)
    pygame.display.set_caption(text+text2)
    # box2d
    colors = { staticBody: self.grey,
        dynamicBody: self.blue,
    }
    for body in (self.myworld.bodies):  
      for fixture in body.fixtures:
        shape = fixture.shape
        if hasattr(shape, 'vertices'):  # rectangle
          vertices = [(body.transform * v) * self.ppm for v in shape.vertices]
          vertices = [((v[0]), (self.screen[1] - v[1])) for v in vertices]
          pygame.draw.polygon(self.window, colors[body.type], vertices, 0)
        else:  # ball
          position = body.transform * shape.pos * self.ppm
          x, y = int(position[0]), int(self.screen[1] - position[1])
          radius = int(shape.radius * self.ppm)
          pygame.draw.circle(self.window, colors[body.type], (x, y), radius, 2)
          x2, y2 = x + radius * math.sin(body.angle), y + radius * math.cos(body.angle)
          pygame.draw.line(self.window, colors[body.type], (x, y), (x2, y2), 2)  # top
  def inputhandling(self):
    for event in pygame.event.get(): 
      if event.type == pygame.QUIT: 
        sys.exit(0) 
      if event.type == pygame.MOUSEMOTION:
        self.mouse = event.pos
        if self.input == "manuel 1":
          self.gripper1 = self.mouse
        if self.input == "manuel 2":
          self.gripper2 = self.mouse
        if self.input == "manuel 3":
          self.gripper3 = self.mouse
      if event.type == pygame.MOUSEBUTTONUP:
        pass
      if event.type == pygame.KEYDOWN:
        if event.key == pygame.K_1: self.taskrotate(self.gripperangle1-22.5)
        if event.key == pygame.K_2: self.taskrotate(self.gripperangle1+22.5)
        if event.key == pygame.K_3: self.taskopengripper()
        if event.key == pygame.K_4: self.taskclosegripper()
        if event.key == pygame.K_5: self.BTstart()
        if event.key == pygame.K_6: self.BTstop()
        if event.key == pygame.K_7:
          if self.input == "manuel 1": self.input="manuel 2"
          elif self.input == "manuel 2": self.input="manuel 1"

  def updateGUI(self):
    self.clock.tick(self.fps)  
    self.window.fill((220, 220, 220))
    self.inputhandling()
    self.paintobjects()

class Game: 
  def __init__(self):
    random.seed()
    self.myGUI = GUI()
    for step in range(10000000):
      self.myGUI.updateGUI()
      self.myGUI.updatePhysics()
      self.myGUI.getpoint("grasp")
      pygame.display.update()  
      
 
if __name__ == "__main__":
  myGame = Game()