示例#1
0
int PVS(int alpha, int beta, int depth)
{
    if(is_draw_by_repetition_or_50_moves()) return DRAW;
    
    if(depth == 0) return quiescence(alpha, beta);
    
    Move movelist[256];
    int n = generate_moves(movelist);
    if(n == 0)
    {
        if(!in_check(turn_to_move))
            return DRAW;
        return LOSING + ply - begin_ply;
    }
    sorting_moves(movelist, n);
    
    int bool_search_pv = 1;
    Move bestmove = 0;

    for(int i = 0; i < n; i += 1)
    {
        Move i_move = movelist[i];
        make_move(i_move);
        int score;
        if(bool_search_pv)
        {
            score = -PVS(-beta, -alpha, depth - 1);
        }
        else
        {
            score = -ZWS(-alpha, depth - 1, 1);
            if(score > alpha)
                score = -PVS(-beta, -alpha, depth - 1);
        }
        unmake_move(i_move);
        
        if(score >= beta)
        {
            hash_save_entry(depth, beta, i_move, MORE_THAN_BETA);
            if(!move_broken(i_move))
            {
                history[board[move_from(i_move)]][move_to(i_move)] = depth * depth;
            }
            return beta;
        }
        if(score > alpha)
        {
            bestmove = i_move;
            alpha = score;
        }
        bool_search_pv = 0;
    }
    if(bestmove != 0)
        hash_save_entry(depth, alpha, bestmove, BETWEEN_ALPHA_AND_BETA);
    else
        hash_save_entry(depth, alpha, 0, LESS_THAN_ALPHA);
    return alpha;
}
示例#2
0
Move search(int depth)
{
    PVS(-1000000, 1000000, depth);
    Entry *entry = hash_get_entry();
    if(entry != NULL) return entry->bestmove;
    return 0;
}
示例#3
0
int Eval(const uint64_t P, const uint64_t O, uint64_t& NodeCounter, const int alpha, const int beta, const int selectivity, int depth, const int empties, CLine* pline)
{
	assert((P & O) == 0);
	assert(-64 <= alpha); assert(alpha <= 64);
	assert(-64 <= beta ); assert(beta  <= 64);
	assert(alpha <= beta);
	assert(0 <= depth); assert(depth <= 60);
	assert(0 <= empties); assert(empties <= 60); assert(empties == Empties(P, O));
	assert(depth <= empties);

	return PVS(P, O, NodeCounter, alpha, beta, selectivity, depth, empties, pline);
}
示例#4
0
static const char*
type_system_v1_docstring(type_system_v1::closure_t* self, type_system_v1::alias tc)
{
    D(kconsole << "type_system.docstring" << endl);
    interface_v1::state_t* iface = nullptr;

    /* Check the type code refers to a valid interface */
    if (!reinterpret_cast<type_system_f_v1::state_t*>(self->d_state)->interfaces_by_typecode->get(TCODE_INTF_CODE(tc), (address_t*)&iface))
        OS_RAISE((exception_support_v1::id)"type_system_v1.bad_code", tc);

    /* Deal with the case where the type code refers to an interface type */
    if (TCODE_IS_INTERFACE(tc))
    {
        return string_copy(iface->rep.autodoc, PVS(heap));
    }
  
    /* Check that within the given interface this is a valid type */
    if (!TCODE_VALID_TYPE (tc, iface))
        OS_RAISE((exception_support_v1::id)"type_system_v1.bad_code", tc);

    type_representation_t* trep = TCODE_WHICH_TYPE(tc, iface);
    return string_copy(trep->autodoc, PVS(heap));
}
示例#5
0
/**
 *  Return a list of all types in the type system.
 */
static naming_context_v1::names
type_system_v1_list(naming_context_v1::closure_t* self)
{
    auto state = reinterpret_cast<type_system_f_v1::closure_t*>(self)->d_state;
    naming_context_v1::names n;
    map_string_address_iterator_v1::closure_t* it = nullptr;

    /* Run through all the interfaces */
    OS_TRY {
        const char* name;
        interface_v1::state_t* tb;
        type_representation_t* trep;

        it = state->interfaces_by_name->iterate();

        while (it->next(&name, (memory_v1::address*)&tb))
        {
            add_name(tb->rep.name, PVS(heap), n);
            /* Run through all the types defined in the current interface */
            for (size_t i = 0; i < tb->num_types; ++i)
            {
                trep = tb->types[i];
                add_qual_name(tb->rep.name, trep->name, PVS(heap), n);
            }
        }
        it->dispose();
    }
    OS_CATCH_ALL {
        if (it)
            it->dispose();
        OS_RAISE((exception_support_v1::id)"heap_v1.no_memory", 0);
    }
    OS_ENDTRY;

    return n;
}
示例#6
0
文件: events.cpp 项目: berkus/metta
static void
events_destroy(events_v1::closure_t* self, event_v1::count ec)
{
    instance_state_t* istate  = self->d_state->inst_state;
    event_count_t* event_count = reinterpret_cast<event_count_t*>(ec);
    vcpu_lock_t lock(istate->vcpu);

    unblock_event(istate, event_count, /*alerted:*/true); // Alert all waiters on this event count.

    event_count->ec_queue.remove();

    if (event_count->ep != NULL_EP)
    {
        if (event_count->prev_notify)
            event_count->prev_notify->set_link(chained_handler_v1::position_after,
                reinterpret_cast<chained_handler_v1::closure_t*>(event_count->next_notify));// oh, man.
        else
        {
            // We were the head of the notify queue, so attach the old handler (may be NULL) to the endpoint.
            istate->dispatcher->attach(event_count->next_notify, event_count->ep);
        }

        if (event_count->next_notify)
            event_count->next_notify->set_link(chained_handler_v1::position_before,
                reinterpret_cast<chained_handler_v1::closure_t*>(event_count->prev_notify));// oh, man.
    }
    lock.unlock();

    // The closedown call to the binder must be outside the critical section: it does a blocking IDC call.
    if (event_count->ep != NULL_EP)
    {
        PVS(binder)->close(event_count->ep);
        self->destroy_channel(event_count->ep);
    }

    // Finally, we can free the event count.
    lock.lock();
    istate->heap->free(reinterpret_cast<memory_v1::address>(event_count)); // oh, man.
}
示例#7
0
/**
 * Look up a type name in this interface.
 */
static type_representation_t* internal_get(type_system_f_v1::state_t* state, const char* name)
{
    interface_v1::state_t* iface = nullptr;
    type_representation_t* result = nullptr;
    stringref_t name_sr(name);
    std::pair<stringref_t, stringref_t> refs = name_sr.split('.');

    // @todo: move this allocation to stringref_t guts?
    if (!refs.second.empty())
        name = string_n_copy(refs.first.data(), refs.first.size(), PVS(heap)); // @todo MEMLEAK

    /* now "name" is just the interface, and "extra" is any extra qualifier */

    if (state->interfaces_by_name->get(name, (address_t*)&iface))
    {
        /* We've found the first component. */
        if (!refs.second.empty())
        {
            for (int i = 0; iface->types[i]; ++i)
                if (refs.second == iface->types[i]->name)
                    result = iface->types[i];

            /* special case if it's an intf type defined by the metainterface */
            if (!result && (iface == &meta_interface))
            {
                result = internal_get(state, refs.second.data()); // this should trigger search in "meta_interface.<something>" but that won't work atm
            }
        }
        else
        {
            /* Otherwise return this interface clp */
            result = &iface->rep;
        }
    }

    return result;
}
示例#8
0
CMoveList::CMoveList(uint64_t P, uint64_t O, uint64_t& NodeCounter, uint64_t BitBoardPossible, int depth, int alpha, const CHashTableValueType& ttValue, const bool pvs)
{
	//if (!pvs) depth -= 2;
	static const int A = 9;
	static const int C = 8;
	static const int H = 7;
	static const int D = 6;
	static const int I = 5;
	static const int G = 4;
	static const int F = 3;
	static const int B = 2;
	static const int E = 1;
	static const int J = 0;
	static const int FIELD_VALUE[64] = {
		A, B, C, D, D, C, B, A,
		B, E, F, G, G, F, E, B,
		C, F, H, I, I, H, F, C,
		D, G, I, J, J, I, G, D,
		D, G, I, J, J, I, G, D,
		C, F, H, I, I, H, F, C,
		B, E, F, G, G, F, E, B,
		A, B, C, D, D, C, B, A,
	};
	static const int PARITY_VALUE[16] = { 0, 20, 0, 10, 1, 10, 2, 10, 3, 5, 3, 4, 3, 4, 3, 4 };
	const uint64_t BBEmpties = ~(P | O);
	const uint64_t empties = Empties(P, O);

	m_Moves = std::vector<CMove>(PopCount(BitBoardPossible));
	int index = 0;

	int sort_depth;
	int min_depth = 9;
    if (empties <= 27) min_depth += (30 - empties) / 3;
    if (depth >= min_depth)
	{
		sort_depth = (depth - 15) / 3;
		if (ttValue.beta < alpha) sort_depth -= 2; 
		sort_depth = BIND(sort_depth, 0, 6);
    }
	else
        sort_depth = -1;

	int sort_alpha = MAX(-64, alpha - 8);

	// Fill MoveList
	while (BitBoardPossible)
	{
		CMove Move;
		Move.move = BitScanLSB(BitBoardPossible);
		RemoveLSB(BitBoardPossible);
		uint64_t flipped = flip(P, O, Move.move);
		Move.P = O ^ flipped;
		Move.O = P ^ flipped ^ (1ULL << Move.move);
		     if (flipped == O) Move.Value = 1 << 31;
		else if ((ttValue.depth > sort_depth) && (Move.move == ttValue.PV)) Move.Value = 1 << 30;
		else if ((ttValue.depth > sort_depth) && (Move.move == ttValue.AV)) Move.Value = 1 << 29;
		else
		{
			uint64_t PossMoves = PossibleMoves(Move.P, Move.O);
			Move.Value = FIELD_VALUE[Move.move];
			Move.Value += PARITY_VALUE[PopCount(BBEmpties & quadrant_mask[Move.move])];
			Move.Value -= PopCount(PossMoves) << 17;
			Move.Value -= PopCount(PossMoves & 0x8100000000000081UL) << 18;
			if (sort_depth < 0)
				Move.Value += PopCount(StableStonesCornerAndCo(Move.O)) << 12;
			else
				Move.Value += PopCount(StableEdges(Move.O, Move.P) & Move.O) << 12;
			Move.Value -= PopCount(OpponentsExposed(Move.P, Move.O)) << 6;
			switch (sort_depth)
			{
			case -1:
				break;
			case 0:
				Move.Value -= EvaluateFeatures(Move.P, Move.O) << 16;
				break;
			case 1:
			case 2:
				Move.Value -= PVS(Move.P, Move.O, NodeCounter, -64, -sort_alpha, NO_SELECTIVITY, sort_depth, empties-1) << 17;
				break;
			default:
				Move.Value -= PVS(Move.P, Move.O, NodeCounter, -64, -sort_alpha, NO_SELECTIVITY, sort_depth, empties-1) << 18;
				CHashTableValueType ttValue2;
				if (TT.LookUp(Move.P, Move.O, ttValue2)) Move.Value += 1 << 21;
				break;
			}
		}
		m_Moves[index++] = Move;
	}
	sort();
	return;
}
示例#9
0
float ScopeDome::getDewPoint(float RH, float T)
{
    T = T + C_OFFSET;
    return solve(PVS, RH / 100 * PVS(T), T) - C_OFFSET;
}
示例#10
0
void CPartida::IterativeDeepening(void)
{
	extern void Print(const char *fmt, ...);
	long ini_it;
	int value,value_i;
	int hasp;
	int MateCount = 0;
	// busqueda con ventana
	int VAlpha,VBeta,Pase;

	int Valor_d1;
	char JugD1[20];

	ValueSearch = 0;
	NodosVisitados = 0;
	cancelado  = 0;
	NodosRepasados = 0;
	SelDepth = 0;
	inicio = TiempoTranscurrido();

	// copiamos tablero principal
	Taux.LoadEPD(T.SaveEPD(),0);

	ResetHistory();
	char PV[1024];
	PV[0] = '\0';
	value = -INFINITO;
	T.CalculaJugadasPosibles();
	HashJ.IncrementaEdad();
	JugadaActual[0] = '\0';
	strcpy(JugadaActual,T.JugadasPosibles[0].ToString());
	Depth = 1;
	value_i = value = PVS(Depth,-INFINITO,+INFINITO,PV,false);
	Valor_d1 = value_i ;
	if(Valor_d1 == INFINITO || Valor_d1 == -INFINITO)
		Valor_d1 = 0;
	JugD1[0] = '\0';

	if(PV[0])
	{
		strcpy(MejorPath,PV);
		PrintInfo(value,PV);
		strcpy(JugadaActual,strtok(PV," "));
		strcpy(JugD1,JugadaActual);
	}
	BFNodeCount[Depth] += NodosVisitados;
	BFHitCount[Depth]++;
	Depth++;
	VAlpha = value-128;
	VBeta = value+128;
	Pase = 0;
	if(Unica)
	{
		PrintInfo(value,PV);
		strcpy(JugadaActual,strtok(PV," u"));
		Print("bestmove %s\n",JugadaActual);
		return;
	}
	while(1)
	{
		if(cancelado)
			break;
		if(tiempo_limite)
		{
			ini_it = TiempoTranscurrido();
			if((ini_it  - inicio) > tiempo_limite) // tiempo excedido
			{
				break;
			}
		}

		if(LimiteProfundidad)
			if(Depth > LimiteProfundidad)
			{
				if(LimiteProfundidad == 1)
					PrintInfo(value,PV);
				break;
			}
		if(Depth > MAXDEPTHC)
			break;

		SelDepth = 0;
		if(Depth > 2)
			Print("info depth %d \n",Depth);
		hasp = value;
		ResetHistory();
		// nos colocamos en la historia
		// preparamos la primera evaluacion
		Taux.CalculaJugadasPosibles();


		value = PVS(Depth,-INFINITO,+INFINITO,PV,false);
		if( cancelado == 1)
			break;

		PrintInfo(value,PV); // end of iteration-> get time to depth
		BFNodeCount[Depth] += NodosVisitados;
		BFHitCount[Depth]++;

		if(value == MATE || value == -MATE)
		{
			strcpy(JugadaActual,"resign");
			break;
		}
		PV[0] = '\0'; // reset de la mejor jugada
		// llevamos la cuenta de iteraciones que damos o recibimos mate
		if(value < (50-MATE) && value > -MATE)
			MateCount++;
		if(value > (MATE-50) && value < MATE)
			MateCount++;

		// a partir de 5 iteraciones de mate podemos dar el resultado por bueno
		if(tiempo_limite)
		if(MateCount >= 5)
			break;

		Depth++;
	}
	// hemos terminado de pensar
	if(JugadaActual[0])
	{
		for(int i=0;JugadaActual[i];i++)
			if(JugadaActual[i] == '#')
				JugadaActual[i] = '\0';

		if(LimiteProfundidad != 1)
			Print("bestmove %s\n",JugadaActual);
	}
	else
	{
		// no tenemos JugadaActual
		Print("bestmove %s\n",T.JugadasPosibles[0].ToString());
	}
	ValueSearch = value;
}
示例#11
0
文件: Nemesis.c 项目: berkus/nemesis
/*
** The Nemesis domain is special - it gets passed 
** only half a comment
** and its Pvs are special.
*/
void Go(Activation_cl *self, 
	VP_clp vp /* IN */,
	Activation_Reason ar /* IN */ ) 
{
    kernel_st      *kst = (kernel_st *)self->st;
    dcb_rw_t       *rwp = vp->st;
    dcb_ro_t       *rop = DCB_RW2RO(rwp);
    ActivationF_cl *actf;

    TRY {

#ifdef __IX86__
	ntsc_entkern(); /* There is an NTSC hack that prevents this
			   from being undone. */
#endif /* __IX86__ */

	/* Direct all output from Nemesis domain through NTSC (or,
	   on Alpha, direct to the hardware) */
	Pvs(err)=Pvs(out)=Pvs(console)=&triv_wr_cl;

	TRC(printf("Nemesis domain entered.\n")); 
	TRC(printf(" + DCBRO at %p\n", rop));
	TRC(printf("      psmask=%qx\n", rop->psmask));
#ifdef __ALPHA__
	TRC(printf("      ps=%qx\n", ntsc_rdps()));
#endif
	TRC(printf(" + DCBRW at %p\n", rwp));
	TRC(printf(" + Activation clp at %p\n", self));
	TRC(printf(" + VP clp at %p\n", vp));
	TRC(printf(" + activationsOn=%d\n", VP$ActivationMode(vp)));
	TRC(printf(" + Activation Reason %d\n", ar));
	TRC(printf(" + PVS        = %p\n", PVS()));
	
	TRC(DUMP_PVS());
	
	{
	    StretchAllocator_clp salloc;
	    StretchAllocator_SizeSeq *sizes;
	    StretchAllocator_StretchSeq *stretches;
	    ThreadsPackage_Stack protoStack;
	    Stretch_clp userStretch;
	    BootDomain_Info *nem_info;
	    ThreadsPackage_clp tp;
	    ThreadF_clp thdf;
#ifdef CONFIG_MEMSYS_EXPT
	    SDriverMod_cl *sdmod;
	    StretchTbl_cl *strtab;
#define MAX_DESCS 5
	    Mem_PMemDesc pmem[MAX_DESCS + 1];
	    Type_Any fany; 
	    flink_t *lk;
	    flist_t *cur; 
	    int i = 0; 

	    /* Create a new stretch driver (physical one for now) */
	    TRC(printf("Creating initial stretch driver and stretch tab.\n"));
	    sdmod  = NAME_FIND("modules>SDriverMod", SDriverMod_clp);
	    strtab = NAME_FIND("sys>StretchTable", StretchTbl_clp);

	    /* Grab the pmem which has been allocated for us by dmgr */
	    for(lk = rop->finfo.next; lk != &rop->finfo; lk = lk->next) {
		cur = (flist_t *)lk; 
		pmem[i].start_addr  = cur->base; 
		pmem[i].frame_width = cur->fwidth; 
		pmem[i].nframes     = 
		    cur->npf >> (cur->fwidth - FRAME_WIDTH); 
		pmem[i].attr        = 0;
		if(++i == MAX_DESCS) 
		    break;
	    }
	    pmem[i].nframes = 0; /* terminate array */

	    ANY_INIT(&fany, Frames_clp, rop->frames);
	    Pvs(sdriver) = SDriverMod$NewPhysical(sdmod, vp, Pvs(heap), 
						  strtab, pmem, &fany);
#endif

	    /* Add our personal frames closure into the sys context */
	    /* XXX SDE: I'm not convinced that this is a good idea; the
	       Nemesis domain (and all other domains) should have a bit of
	       private namespace for this. I was bitten once by a domain
	       using the Nemesis domain's Frames closure directly because
	       its own hadn't overridden it in the namespace. */
	    CX_ADD("sys>Frames", rop->frames, Frames_clp);
	    
	    TRC(eprintf (" + Finding Nemesis StretchAllocator\n"));
	    salloc = NAME_FIND("sys>StretchAllocator", StretchAllocator_clp);
	    TRC(eprintf (" + StretchAlloc = %p\n", salloc));
	    
	    TRC(eprintf (" + Finding parameters\n"));
	    nem_info= NAME_FIND("progs>Nemesis", BootDomain_InfoP);

	    sizes = SEQ_NEW(StretchAllocator_SizeSeq, 3, Pvs(heap));
	    SEQ_ELEM(sizes, 0) = FRAME_SIZE; /* for guard page */
	    SEQ_ELEM(sizes, 1) = nem_info->stackWords*sizeof(word_t);
	    SEQ_ELEM(sizes, 2) = nem_info->pHeapWords*sizeof(word_t);
	    
	    TRC(eprintf (" + Allocating Stretches\n"));
	    stretches = STR_NEWLIST_SALLOC(salloc, sizes);
	    
	    protoStack.guard   = SEQ_ELEM(stretches, 0);
	    protoStack.stretch = SEQ_ELEM(stretches, 1);
	    userStretch        = SEQ_ELEM(stretches, 2);
	    
	    TRC(eprintf (" + Freeing SEQs\n"));
	    SEQ_FREE(sizes);
	    SEQ_FREE(stretches);


#ifdef CONFIG_MEMSYS_EXPT
	    /* Bind and map the stack */
	    {
		Stretch_Size sz; 
		addr_t stackva; 
		int npages; 

		TRC(printf("+ Binding and mapping stack.\n"));
		stackva = STR_RANGE(protoStack.stretch, &sz);
		npages  = sz >> PAGE_WIDTH;
		while(npages--) {
		    StretchDriver$Map(Pvs(sdriver), 
				      protoStack.stretch, stackva);
		    stackva += PAGE_SIZE;
		}

	    }
#endif

	    TRC(printf(" + Setting protection\n"));
	    SALLOC_SETGLOBAL(salloc, protoStack.guard, 0);
	    
	    /* XXX PRB Global write is a bit dodgy to say the least! */
	    SALLOC_SETGLOBAL(salloc, protoStack.stretch, 
			     SET_ELEM(Stretch_Right_Read)    |
			     SET_ELEM(Stretch_Right_Write)   );
	    SALLOC_SETGLOBAL(salloc, userStretch,
			     SET_ELEM(Stretch_Right_Read)    |
			     SET_ELEM(Stretch_Right_Write)   );

	    TRC(printf(" + Finding ThreadsPackage\n"));
	    tp = NAME_FIND("modules>NonPreemptiveThreads",ThreadsPackage_clp); 
	    TRC(printf(" + ThreadsPackage = %p\n", tp));
	    if(!(thdf = ThreadsPackage$New(tp, (addr_t)&NemesisMainThread,
					   (addr_t)kst, &protoStack,
					   userStretch,
					   nem_info->stackWords*sizeof(word_t), 
					   (Pervasives_Init *)PVS(), &actf))) {
		TRC(printf("Nemesis: can't get threads.\n"));
		ntsc_halt();
	    } 
	    /* Set ourselves up to handle memory faults */
	    
	    /* first get/set an event channel for fault notification */
	    rwp->mm_ep = VP$AllocChannel(Pvs(vp));
#if defined(CONFIG_MEMSYS_STD) || defined(CONFIG_MEMSYS_EXPT)
	    {
		MMNotifyMod_cl *mmnmod;
		MMNotify_cl *mmnfy;
		StretchTbl_clp strtable; 

#ifdef CONFIG_MEMSYS_EXPT
		/* get the stretch table */
		TRC(eprintf (" + Finding StretchTable\n"));
		strtable = NAME_FIND("sys>StretchTable", StretchTbl_clp);
#else
		strtable = (StretchTbl_cl *)NULL;
#endif
		mmnmod = NAME_FIND("modules>MMNotifyMod", MMNotifyMod_clp);
		mmnfy = MMNotifyMod$New(mmnmod, vp, Pvs(heap), strtable); 
		CX_ADD("sys>MMNotify", mmnfy, MMNotify_clp);
		ActivationF$Attach(actf, (ChannelNotify_cl *)mmnfy, 
				   rwp->mm_ep);
		
	    }
#endif      /* for non expt, we do all this with a mmentry in main thd */
	}
	TRC(printf(" + yielding to main thread.\n"));

	VP$ActivationsOn(Pvs(vp));
	VP$Yield(Pvs(vp));
	
    } CATCH_Context$NotFound(name) {
	printf("Caught execption Context$NotFound(%s)\n", name);
    } CATCH_ALL {
	printf("Caught exception %s\n", exn_ctx.cur_exception);
    } ENDTRY;
    
    /* XXX we are not happy here; for now, just halt. */
    printf("\nNemesis: Much bogosity!!! halting.\n");
    ntsc_dbgstop();
}
示例#12
0
int PVS(const uint64_t P, const uint64_t O, uint64_t& NodeCounter, const int alpha, const int beta, const int selectivity, const int depth, const int empties, CLine* pline)
{
	assert((P & O) == 0);
	assert(-64 <= alpha); assert(alpha <= 64);
	assert(-64 <= beta ); assert(beta  <= 64);
	assert(alpha <= beta);
	assert(0 <= depth); assert(depth <= 60);
	assert(0 <= empties); assert(empties <= 60); assert(empties == Empties(P, O));
	assert(depth <= empties);
	
	if (depth <= 2 && depth < empties) {
		if (depth == 2) return Midgame::PVS_2(P, O, NodeCounter, alpha, beta, pline);
		if (depth == 1) return Midgame::PVS_1(P, O, NodeCounter, alpha, beta, pline);
		if (depth == 0) return Midgame::Eval_0(P, O, NodeCounter);
	}
	if (empties <= A && depth == empties) return Endgame::PVS_A(P, O, NodeCounter, alpha, beta, empties, pline);

	int lower = alpha;
	int score;
	int bestscore = -65;
	uint8_t BestMove = 64;
	uint64_t BitBoardPossible = PossibleMoves(P, O);
	uint64_t LocalNodeCounter = NodeCounter;
	CHashTableValueType ttValue;
	NodeCounter++;

	if (!BitBoardPossible){
		if (HasMoves(O, P))
			return -PVS(O, P, NodeCounter, -beta, -alpha, selectivity, depth, empties, pline);
		else {
			if (pline) pline->NoMoves();
			return EvalGameOver(P, empties);
		}
	}

	if (!pline && StabilityCutoff_PVS(P, O, alpha, score)) return score;
	if (LookUpTTPV(P, O, ttValue) || LookUpTT(P, O, ttValue))
		if (USE_PV_TTCUT && !pline && UseTTValue(ttValue, alpha, beta, depth, selectivity, score))
			return score;
	if (USE_IID && ttValue.PV == 64) // IID
	{
		int reduced_depth = (depth == empties) ? depth - A : depth - 2;
		if (reduced_depth >= 3)
		{
			PVS(P, O, NodeCounter, -64, 64, 6, reduced_depth, empties, nullptr);
			if (LookUpTTPV(P, O, ttValue))
				if (USE_PV_TTCUT && !pline && UseTTValue(ttValue, alpha, beta, depth, selectivity, score))
					return score;
		}
	}

	CLine * line = nullptr;
	if (pline && pline->size) line = new CLine(pline->size-1);
	CMoveList mvList(P, O, NodeCounter, BitBoardPossible, depth, alpha, ttValue, true);
	for (const auto& mv : mvList)
	{
		if (bestscore == -65)
			score = -PVS(mv.P, mv.O, NodeCounter, -beta, -lower, selectivity, depth-1, empties-1, line);
		else
		{
			score = -ZWS(mv.P, mv.O, NodeCounter, -lower-1, selectivity, depth-1, empties-1);
			if (score > lower && score < beta)
				score = -PVS(mv.P, mv.O, NodeCounter, -beta, -lower, selectivity, depth-1, empties-1, line); // OPTIMIZATION: -lower -> -score
		}
		if (score > bestscore)
		{
			bestscore = score;
			BestMove = mv.move;
			if (line) pline->NewPV(mv.move, line);
			if (score >= beta) break;
			if (score > lower) lower = score;
		}
	}
	
	if (empties-1 <= B)
	{
		UpdateTTPV(P, O, NodeCounter - LocalNodeCounter, alpha, beta, bestscore, depth, NO_SELECTIVITY, BestMove, mvList.BestMove(), mvList.NextBestMove());
		UpdateTT(P, O, NodeCounter - LocalNodeCounter, alpha, beta, bestscore, depth, NO_SELECTIVITY, BestMove, mvList.BestMove(), mvList.NextBestMove());
	}
	else
	{
		UpdateTTPV(P, O, NodeCounter - LocalNodeCounter, alpha, beta, bestscore, depth, selectivity, BestMove, mvList.BestMove(), mvList.NextBestMove());
		UpdateTT(P, O, NodeCounter - LocalNodeCounter, alpha, beta, bestscore, depth, selectivity, BestMove, mvList.BestMove(), mvList.NextBestMove());
	}
	delete line;
	return bestscore;
}